Merge remote-tracking branch 'upstream/master' into merge-webrtc-screenshare-2

Conflicts:
	bigbluebutton-client/resources/config.xml.template
	bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/ScreenshareManager.as
This commit is contained in:
perroned 2016-08-04 18:23:45 +00:00
commit c057111367
91 changed files with 11726 additions and 1750 deletions

View File

@ -117,7 +117,7 @@ class BigBlueButtonActor(val system: ActorSystem,
meetings -= msg.meetingID
log.info("Kick everyone out on meetingId={}", msg.meetingID)
if (m.mProps.isBreakout) {
log.info("Informing parent meeting {} that a breakout room has been ended{}", m.mProps.externalMeetingID, m.mProps.meetingID)
log.info("Informing parent meeting {} that a breakout room has been ended {}", m.mProps.externalMeetingID, m.mProps.meetingID)
eventBus.publish(BigBlueButtonEvent(m.mProps.externalMeetingID,
BreakoutRoomEnded(m.mProps.externalMeetingID, m.mProps.meetingID)))
}

View File

@ -210,7 +210,7 @@ Author: Jesus Federico <jesus@123it.ca>
String url = BigBlueButtonURL.replace("bigbluebutton/","demo/");
String joinURL = getJoinURLwithDynamicConfigXML(username, confname, configXML);
if (joinURL.startsWith("https://") || joinURL.startsWith("https://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<h2>Customized sessions using a dynamic config.xml, submit</h2>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="$$jnlpUrl/lib" href="">
<!--
Keep href empty. Otherwise this jnlp file will always be cached.
http://www.coderanch.com/t/284889/JSP/java/Caching-JNLP
-->
<information>
<title>BigBlueButton Screen Share</title>
<vendor>BigBlueButton</vendor>
</information>
<resources>
<j2se version="1.7+" href="http://java.sun.com/products/autodl/j2se"/>
<jar href="javacv-screenshare-0.0.1.jar" main="true" />
<jar href="ffmpeg.jar" />
</resources>
<resources os="Windows" arch="amd64">
<nativelib href="ffmpeg-windows-x86_64.jar"/>
</resources>
<resources os="Windows" arch="x86">
<nativelib href="$$jnlpUrl/lib/ffmpeg-windows-x86.jar"/>
</resources>
<resources os="Linux" arch="x86_64 amd64">
<nativelib href="ffmpeg-linux-x86_64.jar"/>
</resources>
<resources os="Linux" arch="x86 i386 i486 i586 i686">
<nativelib href="ffmpeg-linux-x86.jar"/>
</resources>
<resources os="Mac OS X">
<nativelib href="ffmpeg-macosx-x86_64.jar"/>
</resources>
<application-desc
name="Desktop Sharing Demo Application"
main-class="org.bigbluebutton.screenshare.client.DeskshareMain">
<argument>$$publishUrl</argument>
<argument>$$serverUrl</argument>
<argument>$$meetingId</argument>
<argument>$$streamId</argument>
<argument>$$fullScreen</argument>
<argument>$$codecOptions</argument>
<argument>$$errorMessage</argument>
</application-desc>
<security><all-permissions/></security>
<update check="always" policy="always"/>
</jnlp>

View File

@ -1,88 +1,103 @@
package org.bigbluebutton.app.screenshare.red5;
import java.util.HashMap;
import java.util.Map;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.Red5;
import org.slf4j.Logger;
import com.google.gson.Gson;
public class Red5AppService {
private static Logger log = Red5LoggerFactory.getLogger(Red5AppService.class, "screenshare");
private Red5AppHandler handler;
/**
* Called from the client to pass us the userId.
*
* We need to do this as we can't have params on the connect call
* as FFMpeg won't be able to connect.
* @param userId
*/
public void setUserId(Map<String, Object> msg) {
String meetingId = Red5.getConnectionLocal().getScope().getName();
String userId = (String) msg.get("userId");
Red5.getConnectionLocal().setAttribute("MEETING_ID", meetingId);
Red5.getConnectionLocal().setAttribute("USERID", userId);
String connType = getConnectionType(Red5.getConnectionLocal().getType());
String connId = Red5.getConnectionLocal().getSessionId();
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", meetingId);
logData.put("userId", userId);
logData.put("connType", connType);
logData.put("connId", connId);
logData.put("event", "user_joining_bbb_screenshare");
logData.put("description", "User joining BBB Screenshare.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("User joining bbb-screenshare: data={}", logStr);
}
private String getConnectionType(String connType) {
if ("persistent".equals(connType.toLowerCase())) {
return "RTMP";
} else if("polling".equals(connType.toLowerCase())) {
return "RTMPT";
} else {
return connType.toUpperCase();
}
}
public void isScreenSharing(Map<String, Object> msg) {
String meetingId = Red5.getConnectionLocal().getScope().getName();
log.debug("Received check if publishing for meeting=[{}]", meetingId);
String userId = (String) Red5.getConnectionLocal().getAttribute("USERID");
handler.isScreenSharing(meetingId, userId);
}
public void startShareRequest(Map<String, Object> msg) {
Boolean record = (Boolean) msg.get("record");
String meetingId = Red5.getConnectionLocal().getScope().getName();
log.debug("Received startShareRequest for meeting=[{}]", meetingId);
String userId = (String) Red5.getConnectionLocal().getAttribute("USERID");
handler.startShareRequest(meetingId, userId, record);
}
public void stopShareRequest(Map<String, Object> msg) {
String meetingId = Red5.getConnectionLocal().getScope().getName();
String streamId = (String) msg.get("streamId");
log.debug("Received stopShareRequest for meeting=[{}]", meetingId);
handler.stopShareRequest(meetingId, streamId);
}
public void setAppHandler(Red5AppHandler handler) {
this.handler = handler;
}
}
package org.bigbluebutton.app.screenshare.red5;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.IConnection;
import org.red5.server.api.Red5;
import org.slf4j.Logger;
import com.google.gson.Gson;
public class Red5AppService {
private static Logger log = Red5LoggerFactory.getLogger(Red5AppService.class, "screenshare");
private Red5AppHandler handler;
/**
* Called from the client to pass us the userId.
*
* We need to do this as we can't have params on the connect call
* as FFMpeg won't be able to connect.
* @param userId
*/
public void setUserId(Map<String, Object> msg) {
String meetingId = Red5.getConnectionLocal().getScope().getName();
String userId = (String) msg.get("userId");
String connType = getConnectionType(Red5.getConnectionLocal().getType());
String sessionId = Red5.getConnectionLocal().getSessionId();
/**
* Find if there are any other connections owned by this user. If we find one,
* that means that the connection is old and the user reconnected. Clear the
* userId attribute so that messages would not be sent in the defunct connection.
*/
Set<IConnection> conns = Red5.getConnectionLocal().getScope().getClientConnections();
for (IConnection conn : conns) {
String connUserId = (String) conn.getAttribute("USERID");
if (connUserId != null && connUserId.equals(userId) && conn.getSessionId().equals(sessionId)) {
conn.removeAttribute("USERID");
}
}
Red5.getConnectionLocal().setAttribute("MEETING_ID", meetingId);
Red5.getConnectionLocal().setAttribute("USERID", userId);
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", meetingId);
logData.put("userId", userId);
logData.put("connType", connType);
logData.put("connId", sessionId);
logData.put("event", "user_joining_bbb_screenshare");
logData.put("description", "User joining BBB Screenshare.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("User joining bbb-screenshare: data={}", logStr);
}
private String getConnectionType(String connType) {
if ("persistent".equals(connType.toLowerCase())) {
return "RTMP";
} else if("polling".equals(connType.toLowerCase())) {
return "RTMPT";
} else {
return connType.toUpperCase();
}
}
public void isScreenSharing(Map<String, Object> msg) {
String meetingId = Red5.getConnectionLocal().getScope().getName();
log.debug("Received check if publishing for meeting=[{}]", meetingId);
String userId = (String) Red5.getConnectionLocal().getAttribute("USERID");
handler.isScreenSharing(meetingId, userId);
}
public void startShareRequest(Map<String, Object> msg) {
Boolean record = (Boolean) msg.get("record");
String meetingId = Red5.getConnectionLocal().getScope().getName();
log.debug("Received startShareRequest for meeting=[{}]", meetingId);
String userId = (String) Red5.getConnectionLocal().getAttribute("USERID");
handler.startShareRequest(meetingId, userId, record);
}
public void stopShareRequest(Map<String, Object> msg) {
String meetingId = Red5.getConnectionLocal().getScope().getName();
String streamId = (String) msg.get("streamId");
log.debug("Received stopShareRequest for meeting=[{}]", meetingId);
handler.stopShareRequest(meetingId, streamId);
}
public void setAppHandler(Red5AppHandler handler) {
this.handler = handler;
}
}

View File

@ -1,7 +1,8 @@
FFMPEG=ffmpeg-2.8.5-1.2-SNAPSHOT-linux-x86.jar
cp ffmpeg-2.8.5-1.2-SNAPSHOT-linux-x86.jar build/libs/ffmpeg-linux-x86-0.0.1.jar
FFMPEG=ffmpeg-3.0.2-1.2-linux-x86.jar
cp $FFMPEG build/libs/ffmpeg-linux-x86-0.0.1.jar
mkdir workdir
rm -rf src
rm -rf workdir
cp build/libs/ffmpeg-linux-x86-0.0.1.jar ../unsigned-jars/ffmpeg-linux-x86-unsigned.jar
ant sign-jar
cp build/libs/ffmpeg-linux-x86-0.0.1.jar ../../../app/jws/lib/ffmpeg-linux-x86.jar

View File

@ -1,6 +1,7 @@
FFMPEG=ffmpeg-2.8.5-1.2-SNAPSHOT-linux-x86_64.jar
FFMPEG=ffmpeg-3.0.2-1.2-linux-x86_64.jar
mkdir workdir
rm -rf workdir
cp $FFMPEG build/libs/ffmpeg-linux-x86_64-0.0.1.jar
cp build/libs/ffmpeg-linux-x86_64-0.0.1.jar ../unsigned-jars/ffmpeg-linux-x86_64-unsigned.jar
ant sign-jar
cp build/libs/ffmpeg-linux-x86_64-0.0.1.jar ../../../app/jws/lib/ffmpeg-linux-x86_64.jar

View File

@ -9,5 +9,8 @@ cp org/bytedeco/javacpp/macosx-x86_64/* ../src/main/resources
cd ..
rm -rf workdir
gradle jar
cp build/libs/ffmpeg-macosx-x86_64-0.0.1.jar ../unsigned-jars/ffmpeg-macosx-x86_64-unsigned.jar
ant sign-jar
cp build/libs/ffmpeg-macosx-x86_64-0.0.1.jar ../../../app/jws/lib/ffmpeg-macosx-x86_64.jar
rm -rf src

View File

@ -1,4 +0,0 @@
gradle jar
ant sign-jar
cp build/libs/ffmpeg-win-x86-0.0.1.jar ../../../app/jws/lib/ffmpeg-windows-x86.jar

View File

@ -1,18 +0,0 @@
FFMPEG=ffmpeg-3.0.2-1.2-windows-x86.jar
mkdir workdir
cp $FFMPEG workdir/ffmpeg-windows-x86.jar
SRC_DIR=src
if [ -d "$SRC_DIR" ]; then
rm -rf src
mkdir -p src/main/resources
fi
cd workdir
jar xvf ffmpeg-windows-x86.jar
cp org/bytedeco/javacpp/windows-x86/*.dll ../src/main/resources
cd ..
rm -rf workdir
gradle jar
ant sign-jar
cp build/libs/ffmpeg-windows-x86-0.0.1.jar ../../../app/jws/lib/ffmpeg-windows-x86.jar
rm -rf src

View File

@ -1,16 +1,17 @@
FFMPEG=ffmpeg-3.0.2-1.2-windows-x86.jar
#mkdir workdir
#cp $FFMPEG workdir/ffmpeg-windows-x86.jar
#rm -rf src
#mkdir -p src/main/resources
#mkdir -p src/main/java
#cd workdir
#jar xvf ffmpeg-windows-x86.jar
#cp org/bytedeco/javacpp/windows-x86/*.dll ../src/main/resources
#cd ..
#rm -rf workdir
#gradle jar
cp $FFMPEG build/libs/ffmpeg-windows-x86-0.0.1.jar
mkdir workdir
cp $FFMPEG workdir/ffmpeg-windows-x86.jar
rm -rf src
mkdir -p src/main/resources
mkdir -p src/main/java
cd workdir
jar xvf ffmpeg-windows-x86.jar
cp org/bytedeco/javacpp/windows-x86/*.dll ../src/main/resources
cd ..
rm -rf workdir
gradle jar
cp build/libs/ffmpeg-windows-x86-0.0.1.jar ../unsigned-jars/ffmpeg-windows-x86-unsigned.jar
ant sign-jar
cp build/libs/ffmpeg-windows-x86-0.0.1.jar ../../../app/jws/lib/ffmpeg-windows-x86.jar
rm -rf src

View File

@ -10,6 +10,7 @@ cp org/bytedeco/javacpp/windows-x86_64/*.dll ../src/main/resources
cd ..
rm -rf workdir
gradle jar
cp build/libs/ffmpeg-windows-x86_64-0.0.1.jar ../unsigned-jars/ffmpeg-windows-x86_64-unsigned.jar
ant sign-jar
cp build/libs/ffmpeg-windows-x86_64-0.0.1.jar ../../../app/jws/lib/ffmpeg-windows-x86_64.jar
rm -rf src

View File

@ -441,10 +441,12 @@
<copy todir="${OUTPUT_DIR}/lib/" >
<fileset dir="${PROD_RESOURCES_DIR}/lib"/>
</copy>
<copy todir="${OUTPUT_DIR}/help/" >
<fileset dir="${PROD_RESOURCES_DIR}/help"/>
</copy>
<copy file="${PROD_RESOURCES_DIR}/BigBlueButtonTest.html" todir="${OUTPUT_DIR}" overwrite="true"/>
<copy file="${PROD_RESOURCES_DIR}/BigBlueButton.html" todir="${OUTPUT_DIR}" overwrite="true"/>
<copy file="${PROD_RESOURCES_DIR}/DeskshareStandalone.html" todir="${OUTPUT_DIR}" overwrite="true"/>
<copy file="${PROD_RESOURCES_DIR}/screenshare-help.html" todir="${OUTPUT_DIR}" overwrite="true"/>
<copy file="${PROD_RESOURCES_DIR}/get_flash_player.gif" todir="${OUTPUT_DIR}" overwrite="true"/>
<copy file="${PROD_RESOURCES_DIR}/bbb.gif" todir="${OUTPUT_DIR}" overwrite="true"/>
<copy file="${PROD_RESOURCES_DIR}/avatar.png" todir="${OUTPUT_DIR}" overwrite="true"/>

View File

@ -55,7 +55,7 @@
vertoHostName="HOST.NAME"
vertoLogin="LOGIN"
vertoPassword="PASSWORD"
help="http://HOST/client/screenshare-help.html"
help="http://HOST/client/help/screenshare-help.html"
/>
<module name="PhoneModule" url="http://HOST/client/PhoneModule.swf?v=VERSION"

View File

@ -0,0 +1,116 @@
/*!
* This stylesheet is a subset of Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
.alert {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
}
.alert h4 {
margin-top: 0;
color: inherit;
}
.alert .alert-link {
font-weight: bold;
}
.alert > p,
.alert > ul {
margin-bottom: 0;
}
.alert > p + p {
margin-top: 5px;
}
.alert-dismissable,
.alert-dismissible {
padding-right: 35px;
}
.alert-dismissable .close,
.alert-dismissible .close {
position: relative;
top: -2px;
right: -21px;
color: inherit;
}
.alert-success {
color: #3c763d;
background-color: #dff0d8;
border-color: #d6e9c6;
}
.alert-success hr {
border-top-color: #c9e2b3;
}
.alert-success .alert-link {
color: #2b542c;
}
.alert-info {
color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
}
.alert-info hr {
border-top-color: #a6e1ec;
}
.alert-info .alert-link {
color: #245269;
}
.alert-warning {
color: #8a6d3b;
background-color: #fcf8e3;
border-color: #faebcc;
}
.alert-warning hr {
border-top-color: #f7e1b5;
}
.alert-warning .alert-link {
color: #66512c;
}
.alert-danger {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
.alert-danger hr {
border-top-color: #e4b9c0;
}
.alert-danger .alert-link {
color: #843534;
}
.close {
float: right;
font-size: 21px;
font-weight: bold;
line-height: 1;
color: #000;
text-shadow: 0 1px 0 #fff;
filter: alpha(opacity=20);
opacity: .2;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
filter: alpha(opacity=50);
opacity: .5;
}
button.close {
-webkit-appearance: none;
padding: 0;
cursor: pointer;
background: transparent;
border: 0;
}
.fade {
opacity: 0;
-webkit-transition: opacity .15s linear;
-o-transition: opacity .15s linear;
transition: opacity .15s linear;
}
.fade.in {
opacity: 1;
}

View File

@ -0,0 +1,241 @@
a {
font-size:.95em;
text-decoration:none;
color:#5f90b0
}
a:hover {
color:#406882
}
* {
margin:0;
padding:0;
font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box
}
.container {
width:1000px;
margin:0 auto
}
.pull-left {
float:left
}
.pull-right {
float:right
}
.row {
min-height:2em;
line-height:2em;
width:100%
}
.span {
float:left;
display:inline;
min-height:1em;
margin:0 .5% 20px
}
.span:four-child {
margin-left:0
}
.span:last-child {
margin-right:0
}
.one {
width:8%
}
.two {
width:16%
}
.three {
width:24%
}
.four {
width:32%
}
.five {
width:40%
}
.six {
width:48%
}
.seven {
width:56%
}
.eight {
width:64%
}
.nine {
width:72%
}
.ten {
width:80%
}
.eleven {
width:88%
}
.twelve {
width:100%
}
.container:after {
content:"\0020";
display:block;
height:0;
clear:both;
visibility:hidden
}
.row:after,.row:before {
content:'\0020';
display:block;
overflow:hidden;
visibility:hidden;
width:0;
height:0
}
.row:after {
clear:both
}
.row {
zoom:1
}
h1,h2,h3,h4,h5,h6 {
font-weight:400;
color:#42444c
}
h1 {
font-size:2.5em;
line-height:1em
}
h2 {
font-size:1.75em;
line-height:1em;
margin-bottom:.375em
}
h3 {
font-size:1.125em
}
h4 {
font-size:1.1em;
font-weight:700
}
.table {
width:100%;
border-spacing:0;
border-collapse:collapse;
text-align:left
}
.table td,.table th {
border-bottom:2px #d1d1d1 solid;
padding:8px 12px
}
.table td {
border-bottom:1px #d4d4d4 solid
}
.table.table-striped tbody tr:nth-child(2n+1) {
background-color:#f3f3f3
}
.table.table-bordered,.table.table-bordered td,.table.table-bordered th {
border:1px solid #CCC
}
.button {
border:0;
border-radius:4px;
outline:0;
cursor:pointer
}
.button.small {
padding:7px 20px;
font-size:.95em
}
.button.large {
padding:10px 45px;
font-size:1.1em
}
.button.primary {
color:#FFF;
background:#5f90b0
}
.button.primary:hover {
background:#5589ab
}
.button.success {
color:#FFF;
background:#4daf7c
}
.button.success:hover {
background:#48a474
}
.button.danger {
color:#FFF;
background:#e6623c
}
.button.danger:hover {
background:#e4572e
}
.navbar {
background:#FFF;
padding:10px;
margin-bottom:60px;
border-bottom:1px #F2F2F2 solid;
text-align:center
}
.navbar h4 {
color:#191919;
font-weight:200
}
.navbar li {
display:inline-block;
padding:.3em 15px 0 0
}
.navbar li:last-child {
padding-right:0
}
.navbar.fixed {
position:fixed;
left:0;
right:0;
top:0
}
.alert {
padding:15px;
margin:10px 0;
border-radius:3px
}
.alert.primary {
background:#e8eff3;
border:1px solid #c5d7e3
}
.alert.success {
background:#daeee4;
border:1px solid #b6dfca
}
.alert.danger {
background:#fdf4f1;
border:1px solid #f7cfc4
}
@media only screen and (min-width:768px) and (max-width:999px) {
.container {
width:768px
}
}
@media only screen and (max-width:767px) {
.container {
width:300px
}
.container .span {
width:100%!important
}
.container .table {
width:100%;
display:block;
overflow:auto
}
}
@media only screen and (min-width:480px) and (max-width:767px) {
.container {
width:420px
}
.container .table {
display:table
}
}

View File

@ -0,0 +1,8 @@
/* latin */
@font-face {
font-family: 'Rokkitt';
font-style: normal;
font-weight: 400;
src: local('Rokkitt'), local('Rokkitt-Regular'), url(http://fonts.gstatic.com/s/rokkitt/v10/o9LMKUV9IIiOIghfS6ZGbALUuEpTyoUstqEm5AMlJo4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}

View File

@ -0,0 +1,284 @@
.FBConnectButton_Small,.FBConnectButton_RTL_Small {
border-left:1px solid #1a356e;
width:54px;
background:-5px -232px!important
}
.FBConnectButton_Small .FBConnectButton_Text {
margin-left:12px!important
}
.dd-digg-ajax-load,.dd-reddit-ajax-load,.dd-dzone-ajax-load,.dd-ybuzz-ajax-load,.dd-twitter-ajax-load,.dd-fbshare-ajax-load,.dd-fblike-ajax-load,.dd-delicious-ajax-load,.dd-sphinn-ajax-load,.dd-gbuzz-ajax-load,.dd-fbshareme-ajax-load,.dd-tweetmeme-ajax-load,.dd-linkedin-ajax-load,.dd-google1-ajax-load {
width:50px;
height:22px;
background:url(../image/ajax-loader.gif) no-repeat center
}
.dd_delicious {
display:block;
color:#000;
font:16px arial;
text-decoration:none;
width:50px;
height:60px;
text-align:center
}
.dd_delicious a:link,.dd_delicious a:visited,.dd_delicious a:active,.dd_delicious a:hover {
text-decoration:none;
color:#000
}
.dd_delicious_normal_image {
text-align:center;
height:60px;
margin-top:3px;
padding:0;
text-align:center;
width:50px;
display:block;
background:url(../image/delicious.png) no-repeat
}
.dd_delicious_compact_image {
text-align:center;
height:22px;
padding:0;
text-align:center;
width:50px;
display:block;
background:url(../image/delicious-compact.png) no-repeat
}
.dd_delicious_compact_image span {
font-size:10px
}
.dd_comments {
text-align:center;
height:16px;
margin-top:3px;
padding:0;
text-align:center;
width:50px;
display:block;
background:url(../image/comments-link.png) no-repeat
}
.dd_button a:link,.dd_button a:visited,.dd_button a:active {
text-decoration:none;
color:#000
}
#dd_ajax_float {
text-align:center;
border:1px solid #bbb;
min-width:55px;
width:auto;
-webkit-border-top-right-radius:5px;
-webkit-border-bottom-right-radius:5px;
-webkit-border-bottom-left-radius:5px;
-webkit-border-top-left-radius:5px;
-moz-border-radius-topright:5px;
-moz-border-radius-bottomright:5px;
-moz-border-radius-bottomleft:5px;
-moz-border-radius-topleft:5px;
border-top-right-radius:5px;
border-bottom-right-radius:5px;
border-bottom-left-radius:5px;
border-top-left-radius:5px;
-moz-background-clip:padding;
-webkit-background-clip:padding-box;
-webkit-box-shadow:1px 0 15px rgba(0,0,0,.2);
-moz-box-shadow:1px 0 15px rgba(0,0,0,.2);
box-shadow:1px 0 15px rgba(0,0,0,.2);
padding:5px;
position:absolute;
display:none;
left:-120px;
font:10px/16px Arial;
background:padding-box #fff
}
#dd_ajax_float .dd_button_v {
width:auto;
height:auto;
line-height:0;
padding:4px 1px
}
#dd_ajax_float iframe {
margin:0
}
#dd_start,#dd_end {
float:left;
clear:both
}
.dd_outer {
width:100%;
height:0;
position:absolute;
top:0;
left:0;
z-index:9999
}
.dd_inner {
margin:0 auto;
position:relative
}
.dd_post_share {
padding:0;
margin-bottom:6px;
display:block
}
.dd_post_share_left {
float:left
}
.dd_post_share_right {
float:right
}
.dd_buttons img {
border:none;
border-width:0;
border-color:#000;
border-style:none
}
#dd_name {
color:#a9a9a9;
font-size:8px;
text-align:center;
margin:8px 0 6px;
padding:0;
line-height:0
}
#dd_name a:link,#dd_name a:visited,#dd_name a:active {
text-decoration:none;
color:#a9a9a9;
border:0
}
#dd_name a:hover {
text-decoration:underline;
color:#a9a9a9;
border:0
}
.dd_button {
float:left;
padding:4px
}
.dd_fblike_xfbml_ajax_left_float {
padding-left:3px
}
.dd_google1_ajax_left_float {
margin-left:-1px
}
.dd_linkedin_ajax_left_float {
margin-left:-2px;
margin-bottom:-5px
}
html {
overflow-y:scroll
}
.dd_button_spacer {
padding-top:8px
}
#dd_comments {
clear:both!important;
width:50px;
height:60px;
font-family:arial
}
#dd_comments a:link,#dd_comments a:visited,#dd_comments a:active,#dd_comments a:hover {
text-decoration:none
}
#dd_comments .clcount {
text-align:center;
color:#444;
display:block;
font-size:20px;
height:34px;
padding:4px 0;
position:relative;
text-align:center;
width:50px;
line-height:24px;
background:url(../image/comments-count.png) no-repeat left top
}
#dd_comments .ccount {
color:#444;
font-size:17px;
text-align:center;
text-decoration:none
}
#dd_comments .clink {
text-align:center;
height:16px;
margin-top:3px;
padding:0;
text-align:center;
width:50px;
display:block;
background:url(../image/comments-link.png) no-repeat
}
.dd_button_extra_v {
padding:1px 4px
}
.st_email_custom {
padding:2px 4px 3px;
border:1px solid #ddd;
-moz-border-radius:2px;
cursor:pointer;
margin-bottom:2px;
background:url(../image/email.png) no-repeat 3px 4px
}
#dd_print_button {
padding:1px 4px 3px;
border:1px solid #ddd;
-moz-border-radius:2px;
cursor:pointer;
margin-bottom:2px;
background:url(../image/print.png) no-repeat 3px 2px
}
#dd_email_text {
padding:0 0 0 18px;
font-size:10px
}
#dd_print_text {
padding:0 0 0 14px;
font-size:10px
}
#dd_print_text a:link,#dd_print_text a:visited,#dd_print_text a:active {
color:#000;
text-decoration:none
}
#dd_print_text a:hover {
color:#00f;
text-decoration:underline
}

View File

@ -0,0 +1,9 @@
@charset "UTF-8";
/*****************************/
/* Hide the WordPress commenting form
/*****************************/
#respond, #commentform, #addcomment, .entry-comments {
display: none;
}

View File

@ -0,0 +1,77 @@
@charset "UTF-8";
/*****************************/
/* Dashboard widget styles
/*****************************/
/*****************************/
/* Normal, page widget styles
displayed here in order rendered
/*****************************/
/* Each post is styled as follows:
<li class="fbc_rc_comment old/even">
<div class="fbc_rc_comment-meta">
<cite class="fbc_rc_comment-author">
<a href="https://www.facebook.com/profile.php?id=FACEBOOKID">
USERNAME
</a>
</cite>
<abbr class="fbc_rc_date">
Date/Time
</abbr>
</div>
<img class="avatar" height="50" width="50" />
<div class="fbc_rc_text">
COMMENT CONTENT
</div>
<div class="fbc_rc_permalink">
<a href=permalink>
Post or Page Title
</a>
</div>
</li>
*/
/* Main widget, which is enclosed in a ul */
#fbc_rc_widget {
width: auto;
margin-left:0 !important; /* override WP default */
}
li.fbc_rc_comment {
min-height: 80px; /* to prevent overlap */
height:100%;
position:relative;
margin:2px;
border-bottom: 1px dotted rgba(88,88,88,1);
}
/* odd and even are so you can style alternating posts differently, if you'd like to */
ul#fbc_rc_widget li.odd { }
ul#fbc_rc_widget li.even {
background-color:rgba(222,222,251,1);
}/* change to float:left to display avatars on the left of comments */
/* encloses both author and date */
.fbc_rc_comment-meta { }
.fbc_rc_comment-author { }
.fbc_rc_date {
font-size:.8em;
}
ul#fbc_rc_widget .avatar {
float: right; /* change to float:left to display avatars on the left of comments */
padding-bottom: 2px;
}
/* main comment text */
.fbc_rc_text {
margin: 5px;
padding-bottom:25px; /* to prevent post titles from overlapping with comment text */
}
.fbc_rc_permalink {
position: absolute;
bottom: 2px;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
.SandboxRoot {
display: none;
}

View File

@ -0,0 +1,35 @@
.home-highlights .home-highlights-content {
overflow: hidden;
margin-bottom: 20px;
color: #464646;
}
.home-highlights h4 {
margin: 0;
font-size: 160%;
line-height: 120%;
color: #30406b;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Open Sans', sans-serif !important;
}
h1.overview {
font-family: "Open Sans","Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,sans-serif;
}
img.aligncenter {
padding: 2px;
}
span.post-comment iframe {
width:150px !important;
}
.post-meta .post-comment{
background: none!important;
padding-left:0px;
}

View File

@ -0,0 +1,13 @@
img.wp-smiley,
img.emoji {
display: inline !important;
border: none !important;
box-shadow: none !important;
height: 1em !important;
width: 1em !important;
margin: 0 .07em !important;
vertical-align: -0.1em !important;
background: none !important;
padding: 0 !important;
}

View File

@ -0,0 +1,75 @@
#headerwrap {
background-image: none;
background-color: #dbebf6;
}
#nav-bar {
background-image: url(http://r2.bigbluebutton.org/wp-content/themes/suco/uploads/bg/bg1.png);
background-color: #30406b;
background-repeat: repeat-x;
}
#upperwrap {
background-image: url(http://r2.bigbluebutton.org/wp-content/themes/suco/uploads/bg/bg1.png);
background-color: #F4F4F4 ;
}
.home-highlightswrap {
background-image: none;
background-color: #fff;
}
#body {
background-image: none;
background-color: #ffffff;
}
.footer-widgetswrap {
background-image: none;
background-color: #262626;
}
#footerwrap {
background-image: none;
background-color: #2a2a2a;
}
h1 {
font-family: Arial, Helvetica, sans-serif;
color: #00445e;
}
h2 {
font-family: Arial, Helvetica, sans-serif;
color: #00445e;
}
h3 {
font-family: Arial, Helvetica, sans-serif;
color: #00445e;
}
h4 {
font-family: Arial, Helvetica, sans-serif;
color: #30406b;
}
h5 {
font-family: Arial, Helvetica, sans-serif;
color: #30406b;
}
h6 {
font-family: Arial, Helvetica, sans-serif;
}
#main-nav a:hover, #main-nav li:hover > a {
color: #fff;
}
#main-nav .current_page_item a, #main-nav .current-menu-item a {
color: #fff;
background-color: #1a243f;
}

View File

@ -0,0 +1,470 @@
@media screen and (max-width: 980px) {
/* set img max-width */
img {
width: auto\9; /* ie8 */
max-width: 100%;
height: auto;
}
/************************************************************************************
STRUCTURE
*************************************************************************************/
.pagewidth, #slider {
max-width: 94%;
}
/* content */
#content {
margin: 7% 0 5%;
}
.sidebar1 #content {
max-width: 67.8%;
}
/* sidebar */
#sidebar {
margin: 7% 0 5%;
max-width: 26.2%;
}
#sidebar .secondary {
max-width: 47%;
}
/************************************************************************************
GRID
*************************************************************************************/
.col4-1,
.col4-2,
.col4-3,
.col3-1,
.col3-2,
.col2-1
{
margin-left: 2%;
}
.col4-1 {
max-width: 23%;
}
.col4-2, .col2-1 {
max-width: 48%;
}
.col4-3 {
max-width: 72%;
}
.col3-1 {
max-width: 31%;
}
.col3-2 {
max-width: 62%;
}
/************************************************************************************
SLIDER
*************************************************************************************/
#slider .slide-feature-image {
max-width: 40%;
margin-right: 3%;
margin-bottom: 2%;
}
#slider .slide-content {
padding: 1% 0 0;
font-size: 100%;
line-height: 130%;
overflow: visible;
}
#slider img {
width: auto\9; /* ie8 */
max-width: 98.5%;
height: auto;
}
/************************************************************************************
LAYOUTS
*************************************************************************************/
/* list post */
.list-post .post,
.list-post .type-page,
.list-post .attachment {
width: 80% !important;
}
/* grid4 */
.grid4 .post {
width: 23.125%;
margin-left: 2.5%;
}
/* grid3 */
.grid3 .post {
width: 31%;
margin-left: 3.5%;
}
/* grid2 */
.grid2 .post {
width: 48%;
margin-left: 3.75%;
}
.social-widget {
color:#30406B;
}
}
@media screen and (max-width: 760px) {
/************************************************************************************
STRUCTURE
*************************************************************************************/
.sidebar1 #content {
width: 100%;
max-width: 100%;
float: none;
clear: both;
}
/* sidebar */
#sidebar {
width: 100%;
max-width: 100%;
float: none;
clear: both;
}
#sidebar .secondary {
width: 48%;
}
/************************************************************************************
HEADER
*************************************************************************************/
/* reset absolute elements to static */
#header .social-widget, #site-logo, #site-description, #header #searchform, #main-nav {
position: static !important;
float: none;
clear: both;
}
/* header */
#header {
height: auto;
}
#header .rss a {
color:#30406B;
}
/* header widget */
.header-widget {
position: static !important;
float: none;
text-align: left;
clear: both;
margin: 0;
}
.header-widget .widget {
margin: 0 0 10px;
}
/* nav bar */
#nav-bar {
height: 10px;
position: static !important;
}
/* social widget */
#header .social-widget {
padding: 5px 0 20px;
}
/* search form */
#header #searchform {
position: absolute;
top: 0;
left: inherit;
right: 0;
width: 150px;
}
#header #searchform #s {
display:none;
/* float: right;
width: 85px;*/
}
#header #searchform input#s:focus {
width: 150px;
}
/* site logo */
#site-logo {
/* margin: 15px 100px 10px 0; */
padding-top:30px;
}
#site-logo a {
font-size: 30px;
}
/* site description */
#site-description {
margin: 0 0 10px;
}
/************************************************************************************
MAIN NAV
*************************************************************************************/
#main-nav {
width:100%;
height: auto;
margin: 10px 0;
padding:10px;
background-color: #30406B;
}
#main-nav a {
}
/************************************************************************************
SLIDER
*************************************************************************************/
#slider .slide-post-title {
font-size: 260%;
line-height: 100%;
margin: 0 0 1%;
}
#slider .slide-content {
font-size: 90%;
}
#slider .caption {
font-size: 80%;
line-height: 120%;
}
/************************************************************************************
HOME HIGHLIGHTS
*************************************************************************************/
.home-highlights .home-highlights-content {
overflow: visible;
}
/************************************************************************************
LAYOUTS
*************************************************************************************/
/* list post */
.list-post .post,
.list-post .type-page,
.list-post .attachment {
width: 78% !important;
}
/************************************************************************************
FOOTER
*************************************************************************************/
.footer-nav {
padding-right: 200px;
}
.footer-nav li {
margin: 0 8px 5px 0;
}
}
@media screen and (max-width: 600px) {
/************************************************************************************
LAYOUTS
*************************************************************************************/
/* list post */
.list-post .post,
.list-post .type-page,
.list-post .attachment {
padding-left: 0 !important;
width: 100% !important;
max-width: 100% !important;
}
.list-post .post-meta {
width: 100%;
text-align: left;
/* position: static; */
display: block;
height: 60px;
}
.list-post .post-meta span {
display: block;
margin: 0 5px 0 0;
border: none;
clear:both;
}
.list-post .post-icon {
/* width: 27px;
height: 30px;
display: block;
float: left;
margin: 2px 14px 5px 0;
border: none;
position: static; */
}
.list-post .post-title {
margin:0;
}
.list-post .post-meta .post-date {
margin-bottom:0px;
}
/* grid4 */
.grid4 .post, .sidebar1 .grid4 .post {
width: 100%;
margin-left: 0;
float: none;
}
/* grid3 */
.grid3 .post, .sidebar1 .grid3 .post {
width: 100%;
margin-left: 0;
float: none;
}
/* action text */
.action-text {
padding: 3% 4%;
}
.action-text .button {
position: static;
clear: both;
width: auto;
margin: 2% 0 0;
}
}
@media screen and (max-width: 480px) {
/* disable webkit text size adjust (for iPhone) */
html {
-webkit-text-size-adjust: none;
}
/************************************************************************************
GRID
*************************************************************************************/
.col4-1,
.col4-2, .col2-1,
.col4-3,
.col3-1,
.col3-2 {
margin-left: 0;
width: 100%;
max-width: 100%;
}
/************************************************************************************
HEADER
*************************************************************************************/
#header hgroup {
text-align: left;
}
/* site logo */
#site-logo {
font-size: 42px;
text-align: left;
padding: 0 90px 0 0;
width: auto;
/* margin: 10px 0; */
padding-top: 30px;
}
/* site description */
#site-description {
text-align: left;
margin: 0 0 10px;
}
/* searchform */
#header #searchform {
top: 0;
left: inherit;
right: 0;
width: 150px;
}
#header #searchform #s {
display:none;
/* float: right;
width: 85px; */
}
/* social widget */
.social-widget {
position: static !important;
margin: 10px 0;
float: none;
padding: 0;
}
/************************************************************************************
LAYOUTS
*************************************************************************************/
/* grid4 */
.grid4 .post {
font-size: 100%;
}
/* grid2 */
.grid2 .post, .sidebar1 .grid2 .post {
width: 100%;
margin-left: 0;
float: none;
}
/* post title */
.post-title {
margin: 0 0 2px !important;
padding-top: 4px;
font-size: 2em !important;
}
/************************************************************************************
POST NAV
*************************************************************************************/
.post-nav .prev, .post-nav .next {
display: block;
width: 100%;
clear: both;
margin-bottom: 10px;
}
/************************************************************************************
COMMENTS
*************************************************************************************/
.commentlist {
padding-left: 0;
}
.commentlist .commententry {
clear: left;
}
.commentlist .avatar {
float: left;
width: 40px;
height: 40px;
margin: 0 10px 10px 0;
}
/* commentlist sub-levels */
.commentlist ul, .commentlist ol {
margin: 0 0 0 7%;
}
/************************************************************************************
FOOTER
*************************************************************************************/
#footer-logo {
position: static !important;
}
.footer-nav {
padding: 0;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,739 @@
/*
Theme Name: Suco Child
Description: Child theme for Suco theme
Template: suco
*/
@import url("../suco/style.css");
/* write custom css below */
/************************************************************************************
CUSTOM TYPOGRAPHY
*************************************************************************************/
p { margin: 0.5em 0 1.0em; padding: 0; line-height: 20px; }
p.imgtext { margin: 0 0 0 0; padding: 0 0 35px 0; font-family: Georgia; font-style: italic; color: #666; font-size: 14px; text-align: center; }
p.subhead { margin-top:-15px; padding-bottom:10px; font-family: Georgia; font-style: italic; color: #666; font-size: 22px; text-align: center;}
p.subfeat { margin-top:-10px; padding-bottom:15px; font-family: Georgia; font-style: italic; color: #666; font-size: 18px; }
.list-post .post-title { margin: 0 0 10px 0; font-size: 28px !important; text-align: left !important; }
#slider p { /* color:#6d8c9e;*/ color: #777;}
.action-text h2, .action-text h3, .action-text h4 { font-size: 26px; line-height: 110%; margin: 0 0 2px; color: #333333; }
#overview h1 { text-align: center; font-size: 48px; padding: 20px 0 0 0; }
h4 { color: #333; }
h5 { color: ##00445E !important;}
/************************************************************************************
CUSTOM BUTTONS
*************************************************************************************/
/* Orange Action Button */
.action-text .button {
background: #e08e19;
background-image: -webkit-gradient(linear, center top, center bottom, from(#f2ab2b), to(#e17b19));
background-image: -webkit-linear-gradient(top, #f2ab2b, #e17b19);
background-image: -moz-linear-gradient(top, #f2ab2b, #e17b19);
background-image: -o-linear-gradient(top, #f2ab2b, #e17b19);
background-image: -ms-linear-gradient(top, #f2ab2b, #e17b19);
background-image: linear-gradient(top, #f2ab2b, #e17b19);
padding: 10px 20px;
font: 26px/100% Rokkitt, Arial, Helvetica, sans-serif;
color: #fff;
text-decoration: none;
position: absolute;
top: 15px;
right: 20px;
border: solid 1px #cb8521;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.5);
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.5);
box-shadow: inset 0 1px 0 rgba(255,255,255,.5);
}
.action-text .button:hover { background:#e08e19; text-decoration: none; }
/*Buttons*/
a.button.green {
border-color: #4e7521;
background: #76ab3b;
background: -moz-linear-gradient(top, #90c356 0%, #649a27 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#90c356), color-stop(100%,#649a27));
background: -webkit-linear-gradient(top, #90c356 0%,#649a27 100%);
background: -o-linear-gradient(top, #90c356 0%,#649a27 100%);
background: -ms-linear-gradient(top, #90c356 0%,#649a27 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#90c356', endColorstr='#649a27',GradientType=0 );
background: linear-gradient(top, #90c356 0%,#649a27 100%);
color: #fff !important;
text-shadow: 0 -1px 0 rgba(0,0,0,.4);
}
a.button.green:hover {
background: #90c356;
}
a.button.green.flat {
background: #76ab3b;
}
/************************************************************************************
HEADER
*************************************************************************************/
#header #searchform #s {
width: 120px;
color: #666;
background: #f2f2f2;
padding-left: 10px !important;
float: right;
-webkit-box-shadow: inset 0 2px 2px rgba(0,0,0,.7), 0 1px 0 rgba(255,255,255,.1);
-moz-box-shadow: inset 0 2px 2px rgba(0,0,0,.7), 0 1px 0 rgba(255,255,255,.1);
box-shadow: inset 0 2px 2px rgba(0,0,0,.7), 0 1px 0 rgba(255,255,255,.1);
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
-webkit-transition: width .7s;
-moz-transition: width .7s;
transition: width .7s;
}
#header a {
color: #fff;
}
a {
color: #30406b;
}
.social-widget {
color:#fff;
}
#header #searchform { display:none;}
/************************************************************************************
MAIN NAVIGATION
*************************************************************************************/
#main-nav {
margin: 0;
padding: 0;
position: absolute;
left: 0;
bottom: 12px;
z-index: 100;
}
#main-nav li {
margin: 0;
padding: 0;
list-style: none;
float: left;
position: relative;
}
/************************************************************************************
MAIN LEVEL NAVIGATION
*************************************************************************************/
#main-nav a {
color: #fff;
display: block;
padding: 5px 15px;
margin: 0 5px 0 0;
text-decoration: none;
font-weight: bold;
}
/* main level link :hover */
#main-nav a:hover, #main-nav li:hover > a {
background: #1a243f;
color: #fff;
-webkit-box-shadow: inset 0 2px 2px rgba(0,0,0,.7), 0 1px 0 rgba(255,255,255,.1);
-moz-box-shadow: inset 0 2px 2px rgba(0,0,0,.7), 0 1px 0 rgba(255,255,255,.1);
box-shadow: inset 0 2px 2px rgba(0,0,0,.7), 0 1px 0 rgba(255,255,255,.1);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
/* current link */
#main-nav .current_page_item a, #main-nav .current-menu-item a {
background: #161211;
color: #c8ad6c;
-webkit-box-shadow: inset 0 2px 2px rgba(0,0,0,.7), 0 1px 0 rgba(255,255,255,.1);
-moz-box-shadow: inset 0 2px 2px rgba(0,0,0,.7), 0 1px 0 rgba(255,255,255,.1);
box-shadow: inset 0 2px 2px rgba(0,0,0,.7), 0 1px 0 rgba(255,255,255,.1);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
/* current link :hover */
#main-nav .current_page_item a:hover, #main-nav .current-menu-item a:hover {
color: #fff;
}
/* sub-levels link */
#main-nav ul a, #main-nav .current_page_item ul a, #main-nav ul .current_page_item a, #main-nav .current-menu-item ul a, #main-nav ul .current-menu-item a, #main-nav li:hover > ul a {
color: #666;
font-weight: normal;
padding: 7px 0 7px 15px;
margin: 0;
width: 180px;
background: none;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
/* sub-levels link :hover */
#main-nav ul a:hover, #main-nav .current_page_item ul a:hover, #main-nav ul .current_page_item a:hover, #main-nav .current-menu-item ul a:hover, #main-nav ul .current-menu-item a:hover, #main-nav li:hover > ul a:hover {
color: #00445E;
background-color: #EFF8FB;
}
/* sub-level ul */
#main-nav ul {
margin-top: -3px;
padding: 5px 0;
list-style: none;
position: absolute;
background: #fff;
border: solid 1px #ccc;
z-index: 100;
display: none;
-webkit-border-top-left-radius: 0px;
-webkit-border-top-right-radius: 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
-moz-border-radius-topleft: 0px;
-moz-border-radius-topright: 5px;
-moz-border-radius-bottomright: 5px;
-moz-border-radius-bottomleft: 5px;
border-top-left-radius: 0px;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
}
#main-nav ul li {
background: none;
padding: 0;
margin: 0;
display: block;
}
/* sub-sub-level dropdown */
#main-nav ul ul {
left: 190px;
top: -2px;
}
/* show dropdown ul on hover */
#main-nav li:hover > ul {
display: block;
}
/************************************************************************************
PAGE NAVIGATION (BLOG)
*************************************************************************************/
.pagenav a {
color: white;
text-decoration: none;
background: #273d6c;
padding: 2px 6px;
border: solid 1px #1a2744;
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2);
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2);
box-shadow: inset 0 1px 0 rgba(255,255,255,.2);
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border-image: initial;
}
.pagenav a:hover {
background:#142240;
}
a.button.blue-opaque {
border: 1px solid #7996ac;
background: #b1c8da;
color: #fff !important;
text-shadow: 0 1px #71AEC2;
}
a.button.blue-opaque:hover {
background: #9ab3c7;
}
a.button.blue-opaque.flat {
background: #76ab3b;
}
input[type="reset"], input[type="submit"] {
background: #273D6C !important;
border: 1px solid #1A2744;
}
/************************************************************************************
BANNER
*************************************************************************************/
#headerwrap {
background-color: #EFF8FB;
}
.welcome-message {
font: 280%/110% Rokkitt, Arial, Helvetica, sans-serif;
color: #573a1e;
text-align: center;
margin-bottom: 35px;
}
/* Slider */
#slider .slides li {
background: none !important;
position: relative;
color:#6D8C9E;;
margin: 0;
padding: 0;
width: 978px;
list-style-type:square;
float: left;
}
/************************************************************************************
FEATURE HIGHLIGHTS
*************************************************************************************/
.home-highlightswrap {
position: relative;
background: #3D2C29 url(images/dash-bg.png) repeat-x 0 bottom;
border-top: 0 !important;
-webkit-box-shadow: inset 0 2px 3px rgba(0,0,0,.3);
-moz-box-shadow: inset 0 2px 3px rgba(0,0,0,.3);
box-shadow: inset 0 2px 3px rgba(0,0,0,.3);
}
.home-highlights .home-highlights-content {
color:#777 !important;
}
.home-highlights .icon {
padding: 0 0 0 5px;
float: left;
position: relative;
}
/************************************************************************************
FEATURE PAGE
*************************************************************************************/
#roles { height:520px; }
#roles .first{ margin-left:0px; padding: 10px 0px 10px 0px; clear:left;}
#roles .col3-1{ padding: 10px 0px 0px 0px; }
#presentations { height:425px; padding-top:45px; }
#listeners { padding-top:45px; height:350px;}
#chat { padding-top:45px; height:350px;}
#deskshare { padding-top:45px; height:375px;}
#small-f {padding-top:45px; height:325px; }
/************************************************************************************
OPEN SOURCE COMPONENTS PAGE
*************************************************************************************/
#os_cmp img { float:left; padding: 0 15px 0 0; }
#os_cmp .first{ margin-left:0px; padding: 10px 20px 10px 0px; clear:left; max-width: 45%;}
#os_cmp .col2-1{ padding: 15px 20px 0px 0px; max-width: 45%; }
/************************************************************************************
VIDEO PAGE
*************************************************************************************/
/* Tutorial Videos */
#vid_tut{ height:100%; width:auto; display:block; float:left; }
#vid_tut img { border: 5px solid #fff;}
#vid_tut .first { margin-left:0px; clear:left; }
#vid_tut .col2-1{ max-width: 42%; }
/* Developer Videos */
#vid_dev{ height:100%; width:auto; display:block; float:left; margin:20px 0 0 0; }
#vid_dev img { border: 5px solid #fff;}
#vid_dev .first { margin-left:0px !important; clear:left; }
#vid_dev .col2-1{ max-width: 42%; }
.play { background:transparent url(/wp-content/uploads/2011/12/play.png) no-repeat; padding: 8px 6px 15px 40px; font-weight:bold; }
.youvid{ border:5px white; }
/************************************************************************************
SUPPORT PAGE
*************************************************************************************/
#c_support .first{ margin-left:0px; padding: 10px 20px 10px 0px; clear:left; max-width: 45%; height:215px;}
#c_support .col2-1 { padding: 15px 20px 0px 0px; max-width: 45%; height:215px;}
#open_source .first{ margin-left:0px; padding: 10px 20px 10px 0px; clear:left; max-width: 45%;}
#open_source .col2-1{ padding: 15px 20px 0px 0px; max-width: 45%; }
/************************************************************************************
COMMERCIAL SUPPORT
*************************************************************************************/
.heading {
padding: 8px 6px 5px 50px;
font-family: Arial, Helvetica, sans-serif;
color: #30406B;
font-size: 1.6em;
line-height: 1.4em;
font-weight: normal;
text-shadow: 0 2px 0 rgba(0, 0, 0, .15);
}
/************************************************************************************
OPEN SOURCE INTEGRATIONS
*************************************************************************************/
.wordpress { background:transparent url(/wp-content/uploads/2011/12/wordpress_icon.png) no-repeat; }
.joomla { background:transparent url(/wp-content/uploads/2011/12/joomla_icon.png) no-repeat; }
.drupal { background:transparent url(/wp-content/uploads/2011/12/drupal_icon.png) no-repeat; }
.moodle { background:transparent url(/wp-content/uploads/2011/12/moodle_icon.png) no-repeat; }
.sakai{ background:transparent url(/wp-content/uploads/2011/12/sakai_icon.png) no-repeat; }
.tiki { background:transparent url(/wp-content/uploads/2011/12/tiki_icon.png) no-repeat; }
.efront { background:transparent url(/wp-content/uploads/2011/12/efront_icon.png) no-repeat; }
.redmine { background:transparent url(/wp-content/uploads/2011/12/redmine_icon.png) no-repeat; }
.foswiki { background:transparent url(/wp-content/uploads/2011/12/foswiki_icon.png) no-repeat; }
.atutor { background:transparent url(/wp-content/uploads/2012/04/atutor_logo.png) no-repeat; }
.fedena { background:transparent url(/wp-content/uploads/2012/09/fedena_logo.png) no-repeat; }
/************************************************************************************
BLOG
*************************************************************************************/
.post-title a {
color: #30406B;
text-decoration: none;
font-weight:bold;
}
.post-image img, .widget .feature-posts-list .post-img, .widget .flickr_badge_image img, .commentlist .avatar, .recent-comments-list .avatar, #body .gallery img, .attachment img {
adding: 2px;
background: white;
border: solid 2px #E3E3E3;
border-bottom-color: #DDD;
border-image: initial;
}
.list-post .post-image {
margin: 15px 0 15px;
}
.sidebar1 .list-post .post, .sidebar1 .list-post .hentry, .sidebar1 .list-post .type-page, .sidebar1 .list-post .attachment {
padding-left: 130px;
width: 550px;
position: relative;
margin-bottom: 100px;
}
.h_overview { text-align:center; padding-bottom:20px;}
.header-widget h1, .header-widget h2, .header-widget h3, .header-widget h4, .header-widget h5, .header-widget h6 {
margin: 0;
padding: 0;
color: #00445E;
}
.blue {color: #00445E !important; }
input[type="text"], input[type="password"], textarea, input[type="search"] {
background: #eee;
border: 1px solid #cccccc;
padding: 6px 10px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-appearance: none;
border-image: initial;
}
.post-nav span span { background: #273D6C !important; border:1px solid #1a2744; }
.commentlist .comment-reply-link { background: #273D6C !important; border:1px solid #1a2744; }
/************************************************************************************
SIDEBAR WIDGET
*************************************************************************************/
.action-text {
background: #f9f9f8;
border: solid 1px #ccc;
position: relative;
margin-top: 40px;
padding: 15px 200px 15px 25px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 0 1px 0 rgba(0,0,0,.1);
-moz-box-shadow: 0 1px 0 rgba(0,0,0,.1);
box-shadow: 0 1px 0 rgba(0,0,0,.1);
clear:both;
}
#text-4{
border-bottom: solid 1px #CCC;
padding: 40px 0 30px;
border: 1px solid #CCC;
border: 1px solid white;
-moz-box-shadow: 0 0 5px #999;
-webkit-box-shadow: 0 0 5px #999;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
background: -moz-linear-gradient(top, whiteSmoke, #F0F0F0);
background: -webkit-gradient(linear, left top, left bottom, from(whiteSmoke), to(#F0F0F0));
}
#text-4{ padding: 20px !important; }
#themify-feature-posts-3, #themify-recent-comments-3{
border-bottom: solid 1px #CCC;
padding: 20px !important;
border: 1px solid #CCC;
border: 1px solid white;
-moz-box-shadow: 0 0 5px #999;
-webkit-box-shadow: 0 0 5px #999;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
background: -moz-linear-gradient(top, whiteSmoke, #F0F0F0);
background: -webkit-gradient(linear, left top, left bottom, from(whiteSmoke), to(#F0F0F0));
}
#sidebar .widget_text{
border-bottom: solid 1px #CCC;
padding: 20px !important;
border: 1px solid #CCC;
border: 1px solid white;
-moz-box-shadow: 0 0 5px #999;
-webkit-box-shadow: 0 0 5px #999;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
background: -moz-linear-gradient(top, whiteSmoke, #F0F0F0);
background: -webkit-gradient(linear, left top, left bottom, from(whiteSmoke), to(#F0F0F0));
}
#sidebar .widget_ns_mailchimp {
border-bottom: solid 1px #CCC;
padding: 20px !important;
border: 1px solid #CCC;
border: 1px solid white;
-moz-box-shadow: 0 0 5px #999;
-webkit-box-shadow: 0 0 5px #999;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
background: -moz-linear-gradient(top, whiteSmoke, #F0F0F0);
background: -webkit-gradient(linear, left top, left bottom, from(whiteSmoke), to(#F0F0F0));
}
#ns_widget_mailchimp-email-3 {
background-color:#fff;
width:90%;
margin-bottom:10px;
}
/************************************************************************************
WRAPS
*************************************************************************************/
.sub_wrap{
display: block;
float:left;
padding: 20px;
border: 1px solid #CCC;
border: 1px solid white;
-moz-box-shadow: 0 0 5px #999;
-webkit-box-shadow: 0 0 5px #999;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
background: -moz-linear-gradient(top, whiteSmoke, #F0F0F0);
margin: 0 0 40px 35px;
background: -webkit-gradient(linear, left top, left bottom, from(whiteSmoke), to(#F0F0F0));
}
#upperwrap {
background-image: url(http://r2.bigbluebutton.org/wp-content/themes/suco/uploads/bg/bg1.png);
background-color: #fff;
background: #fff url(images/fibre-bg.png);
border-bottom: none !important;
padding: 40px 0 30px;
}
/************************************************************************************
Turning Off ShareThis On The Slider Section
*************************************************************************************/
.slide-content .stButton .facebook {
display:none;
margin:0;
padding:0;
}
.slide-content .stButton .twitter {
display:none;
margin:0;
padding:0;
}
.slide-content .stButton .email {
display:none;
margin:0;
padding:0;
}
.slide-content .stButton .sharethis {
display:none;
margin:0;
padding:0;
}
/************************************************************************************
Turning Off ShareThis On The Highlights Section
*************************************************************************************/
.home-highlights .home-highlights-content .stButton .facebook {
display:none;
margin:0;
padding:0;
}
.home-highlights .home-highlights-content .stButton .twitter {
display:none;
margin:0;
padding:0;
}
.home-highlights .home-highlights-content .stButton .email {
display:none;
margin:0;
padding:0;
}
.home-highlights .home-highlights-content .stButton .sharethis {
display:none;
margin:0;
padding:0;
}
/************************************************************************************
CUSTOM TABLE
*************************************************************************************/
table.table2{
font-family:Arial, Helvetica, sans-serif;
font-size: 14px;
font-style: normal;
font-weight: normal;
line-height: 22px;
border-collapse:collapse;
border-right:1px dotted #e0e0e0;
border-left:1px dotted #e0e0e0;
border-bottom:1px dotted #e0e0e0;
}
.table2 thead th {
padding:20px 10px 20px 10px;
color:#fff;
font-size: 20px;
background-color:#252625;
font-weight:normal;
text-shadow:1px 1px 1px #000;
}
.table2 thead th:empty{
background:transparent;
-moz-box-shadow:none;
-webkit-box-shadow:none;
box-shadow:none;
}
.table2 thead :nth-last-child(1){
border-right:none;
}
.table2 thead :first-child,
.table2 tbody :nth-last-child(1){
border:none;
}
.table2 tbody th{
vertical-align:top;
color: #00445E;
padding:25px;
text-shadow:1px 1px 1px #ccc;
background-color:#eff8fb;
}
.table2 tbody td{
padding:25px;
background: -moz-linear-gradient(100% 25% 90deg, #fefefe, #f9f9f9);
background: -webkit-gradient(linear, 0% 0%, 0% 25%, from(#f9f9f9), to(#fefefe));
border-right:1px dotted #e0e0e0;
text-shadow:-1px 1px 1px #fff;
color:#777;
}
/************************************************************************************
FOOTER
*************************************************************************************/
#footer-logo { display:none; }
#footer { clear: both; position: relative; min-height: 25px; color: #B3AEAC; padding: 15px 0; }
/* ------------------
styling for the tables
------------------ */
#hor-minimalist-b
{
font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif;
font-size: 12px;
background: #fff;
/* margin: 45px; */
width: 480px;
border-collapse: collapse;
text-align: left;
}
#hor-minimalist-b th
{
font-size: 18px;
font-weight: bold;
color: #00445E;
padding: 10px 8px;
border-bottom: 2px solid #00445E;
}
#hor-minimalist-b td
{
border-bottom: 1px solid #ccc;
color: #777;
padding: 6px 8px;
}
#hor-minimalist-b tbody tr:hover td
{
color: #444;
}
/* My changes */
p.nocomments, #respond h3 { display: none; }
.clearfix {
*zoom: 1;
&:before,
&:after {
display: table;
content: "";
}
&:after {
clear: both;
}
}
.home-highlights div:nth-child(7) {
margin-left:0 !important;
}
.home-highlights div:nth-child(8) {
margin-left:30px;
clear:none !important;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,584 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,maximum-scale=1">
<title> Videos</title>
<link rel="alternate" type="application/rss+xml" title="BigBluebutton RSS Feed" href="http://bigbluebutton.org/feed/">
<link rel="stylesheet" href="http://bigbluebutton.org/wp-content/themes/suco/themify/css/shortcodes.css" type="text/css" media="screen">
<link href="http://bigbluebutton.org/wp-content/themes/suco-child/style.css" rel="stylesheet" type="text/css">
<!-- google font -->
<link href="http://fonts.googleapis.com/css?family=Rokkitt&amp;v1" rel="stylesheet" type="text/css">
<!-- media queries -->
<link rel="stylesheet" href="http://bigbluebutton.org/wp-content/themes/suco/media-queries.css" type="text/css">
<!-- media-queries.js (fallback) -->
<!--[if lt IE 9]>
<script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>
<![endif]-->
<!-- html5.js -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- jquery -->
<script src="http://platform.twitter.com/widgets.js" id="twitter-wjs"></script><script src="http://www.google-analytics.com/ga.js" async="" type="text/javascript"></script><script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>
<!-- audio player -->
<script type="text/javascript" src="http://bigbluebutton.org/wp-content/themes/suco/js/audio-player.js"></script>
<script type="text/javascript">
AudioPlayer.setup("http://bigbluebutton.org/wp-content/themes/suco/player.swf", {
width: '90%',
transparentpagebg: 'yes'
});
</script>
<!-- comment-reply js -->
<!-- wp_header -->
<script type="text/javascript">
window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/72x72\/","ext":".png","source":{"concatemoji":"http:\/\/bigbluebutton.org\/wp-includes\/js\/wp-emoji-release.min.js?ver=21e517e0ddb856c019dcb251670c2be4"}};
!function(a,b,c){function d(a){var c,d,e,f=b.createElement("canvas"),g=f.getContext&&f.getContext("2d"),h=String.fromCharCode;if(!g||!g.fillText)return!1;switch(g.textBaseline="top",g.font="600 32px Arial",a){case"flag":return g.fillText(h(55356,56806,55356,56826),0,0),f.toDataURL().length>3e3;case"diversity":return g.fillText(h(55356,57221),0,0),c=g.getImageData(16,16,1,1).data,d=c[0]+","+c[1]+","+c[2]+","+c[3],g.fillText(h(55356,57221,55356,57343),0,0),c=g.getImageData(16,16,1,1).data,e=c[0]+","+c[1]+","+c[2]+","+c[3],d!==e;case"simple":return g.fillText(h(55357,56835),0,0),0!==g.getImageData(16,16,1,1).data[0];case"unicode8":return g.fillText(h(55356,57135),0,0),0!==g.getImageData(16,16,1,1).data[0]}return!1}function e(a){var c=b.createElement("script");c.src=a,c.type="text/javascript",b.getElementsByTagName("head")[0].appendChild(c)}var f,g,h,i;for(i=Array("simple","flag","unicode8","diversity"),c.supports={everything:!0,everythingExceptFlag:!0},h=0;h<i.length;h++)c.supports[i[h]]=d(i[h]),c.supports.everything=c.supports.everything&&c.supports[i[h]],"flag"!==i[h]&&(c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&c.supports[i[h]]);c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&!c.supports.flag,c.DOMReady=!1,c.readyCallback=function(){c.DOMReady=!0},c.supports.everything||(g=function(){c.readyCallback()},b.addEventListener?(b.addEventListener("DOMContentLoaded",g,!1),a.addEventListener("load",g,!1)):(a.attachEvent("onload",g),b.attachEvent("onreadystatechange",function(){"complete"===b.readyState&&c.readyCallback()})),f=c.source||{},f.concatemoji?e(f.concatemoji):f.wpemoji&&f.twemoji&&(e(f.twemoji),e(f.wpemoji)))}(window,document,window._wpemojiSettings);
</script>
<style type="text/css">
img.wp-smiley,
img.emoji {
display: inline !important;
border: none !important;
box-shadow: none !important;
height: 1em !important;
width: 1em !important;
margin: 0 .07em !important;
vertical-align: -0.1em !important;
background: none !important;
padding: 0 !important;
}
</style>
<link rel="stylesheet" id="fbComments_hideWpComments-css" href="http://bigbluebutton.org/wp-content/plugins/facebook-comments-for-wordpress/css/facebook-comments-hidewpcomments.css?ver=3.1.3" type="text/css" media="all">
<link rel="stylesheet" id="fbc_rc_widgets-style-css" href="http://bigbluebutton.org/wp-content/plugins/facebook-comments-for-wordpress/css/facebook-comments-widgets.css?ver=21e517e0ddb856c019dcb251670c2be4" type="text/css" media="all">
<link rel="stylesheet" id="digg-digg-css" href="http://bigbluebutton.org/wp-content/plugins/digg-digg/css/diggdigg-style.css?ver=5.3.6" type="text/css" media="screen">
<script type="text/javascript" src="http://bigbluebutton.org/wp-includes/js/jquery/jquery.js?ver=1.12.4"></script>
<script type="text/javascript" src="http://bigbluebutton.org/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1"></script>
<script type="text/javascript" src="http://bigbluebutton.org/wp-content/plugins/mailchimp-widget/js/mailchimp-widget-min.js?ver=21e517e0ddb856c019dcb251670c2be4"></script>
<link rel="https://api.w.org/" href="http://bigbluebutton.org/wp-json/">
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://bigbluebutton.org/xmlrpc.php?rsd">
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://bigbluebutton.org/wp-includes/wlwmanifest.xml">
<link rel="canonical" href="http://bigbluebutton.org/videos/">
<link rel="shortlink" href="http://bigbluebutton.org/?p=6">
<link rel="alternate" type="application/json+oembed" href="http://bigbluebutton.org/wp-json/oembed/1.0/embed?url=http%3A%2F%2Fbigbluebutton.org%2Fvideos%2F">
<link rel="alternate" type="text/xml+oembed" href="http://bigbluebutton.org/wp-json/oembed/1.0/embed?url=http%3A%2F%2Fbigbluebutton.org%2Fvideos%2F&amp;format=xml">
<script type="text/javascript">
(function(url){
if(/(?:Chrome\/26\.0\.1410\.63 Safari\/537\.31|WordfenceTestMonBot)/.test(navigator.userAgent)){ return; }
var addEvent = function(evt, handler) {
if (window.addEventListener) {
document.addEventListener(evt, handler, false);
} else if (window.attachEvent) {
document.attachEvent('on' + evt, handler);
}
};
var removeEvent = function(evt, handler) {
if (window.removeEventListener) {
document.removeEventListener(evt, handler, false);
} else if (window.detachEvent) {
document.detachEvent('on' + evt, handler);
}
};
var evts = 'contextmenu dblclick drag dragend dragenter dragleave dragover dragstart drop keydown keypress keyup mousedown mousemove mouseout mouseover mouseup mousewheel scroll'.split(' ');
var logHuman = function() {
var wfscr = document.createElement('script');
wfscr.type = 'text/javascript';
wfscr.async = true;
wfscr.src = url + '&r=' + Math.random();
(document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(wfscr);
for (var i = 0; i < evts.length; i++) {
removeEvent(evts[i], logHuman);
}
};
for (var i = 0; i < evts.length; i++) {
addEvent(evts[i], logHuman);
}
})('//bigbluebutton.org/?wordfence_logHuman=1&hid=4A32C1A289EB14E197561B7A60F05460');
</script>
<style type="text/css">
#headerwrap {
background-image: none;
background-color: #dbebf6;
}
#nav-bar {
background-image: url(http://r2.bigbluebutton.org/wp-content/themes/suco/uploads/bg/bg1.png);
background-color: #30406b;
background-repeat: repeat-x;
}
#upperwrap {
background-image: url(http://r2.bigbluebutton.org/wp-content/themes/suco/uploads/bg/bg1.png);
background-color: #F4F4F4 ;
}
.home-highlightswrap {
background-image: none;
background-color: #fff;
}
#body {
background-image: none;
background-color: #ffffff;
}
.footer-widgetswrap {
background-image: none;
background-color: #262626;
}
#footerwrap {
background-image: none;
background-color: #2a2a2a;
}
h1 {
font-family: Arial, Helvetica, sans-serif;
color: #00445e;
}
h2 {
font-family: Arial, Helvetica, sans-serif;
color: #00445e;
}
h3 {
font-family: Arial, Helvetica, sans-serif;
color: #00445e;
}
h4 {
font-family: Arial, Helvetica, sans-serif;
color: #30406b;
}
h5 {
font-family: Arial, Helvetica, sans-serif;
color: #30406b;
}
h6 {
font-family: Arial, Helvetica, sans-serif;
}
#main-nav a:hover, #main-nav li:hover > a {
color: #fff;
}
#main-nav .current_page_item a, #main-nav .current-menu-item a {
color: #fff;
background-color: #1a243f;
}
</style>
<link href="http://www.bigbluebutton.org/wp-content/themes/suco/uploads/favicon/favicon.png" rel="shortcut icon">
<!-- custom css -->
<style type="text/css">.home-highlights .home-highlights-content {
overflow: hidden;
margin-bottom: 20px;
color: #464646;
}
.home-highlights h4 {
margin: 0;
font-size: 160%;
line-height: 120%;
color: #30406b;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Open Sans', sans-serif !important;
}
h1.overview {
font-family: "Open Sans","Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,sans-serif;
}
img.aligncenter {
padding: 2px;
}
span.post-comment iframe {
width:150px !important;
}
.post-meta .post-comment{
background: none!important;
padding-left:0px;
}</style>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-9394204-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<script src="//bigbluebutton.org/?wordfence_logHuman=1&amp;hid=4A32C1A289EB14E197561B7A60F05460&amp;r=0.7928567202447322" async="" type="text/javascript"></script><script src="https://platform.twitter.com/js/timeline.d5ee0c695f13b8ee1c5f4f85a8c10e39.js" async="" charset="utf-8" type="text/javascript"></script></head>
<body class="page page-id-6 page-template-default">
<div id="pagewrap">
<div id="headerwrap">
<header id="header" class="pagewidth">
<hgroup>
<div id="site-logo"><a href="http://bigbluebutton.org"><img src="http://bigbluebutton.org/wp-content/themes/suco/themify/img.php?src=/wp-content/uploads/2011/12/logo.png&amp;w=330&amp;h=71" alt="BigBluebutton" height="71" width="330"></a></div>
<h2 id="site-description"></h2>
</hgroup>
<nav>
<ul id="main-nav" class="main-nav"><li id="menu-item-30" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-30"><a href="http://bigbluebutton.org/">Home</a></li>
<li id="menu-item-2665" class="menu-item menu-item-type-post_type menu-item-object-page current-menu-ancestor current-menu-parent current_page_parent current_page_ancestor menu-item-has-children menu-item-2665"><a href="http://bigbluebutton.org/overview/">Overview</a>
<ul class="sub-menu">
<li id="menu-item-2667" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2667"><a href="http://bigbluebutton.org/overview/">Overview</a></li>
<li id="menu-item-1424" class="menu-item menu-item-type-post_type menu-item-object-page current-menu-item page_item page-item-6 current_page_item menu-item-1424"><a href="http://bigbluebutton.org/videos/">Videos</a></li>
<li id="menu-item-3083" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-3083"><a href="http://bigbluebutton.org/accessibility/">Accessibility</a></li>
<li id="menu-item-1538" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-1538"><a href="http://bigbluebutton.org/history/">History of BigBlueButton</a></li>
</ul>
</li>
<li id="menu-item-18" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-18"><a href="http://demo.bigbluebutton.org/">Demo</a></li>
<li id="menu-item-494" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-has-children menu-item-494"><a href="http://bigbluebutton.org/support/">Support</a>
<ul class="sub-menu">
<li id="menu-item-1088" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-1088"><a href="http://bigbluebutton.org/support/">Support</a></li>
<li id="menu-item-231" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-231"><a href="http://bigbluebutton.org/components/">Open Source Components</a></li>
<li id="menu-item-1614" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-1614"><a href="http://bigbluebutton.org/open-source-integrations/">Open Source Integrations</a></li>
<li id="menu-item-713" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-713"><a href="http://bigbluebutton.org/commercial-support/">Commercial Support</a></li>
</ul>
</li>
<li id="menu-item-337" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-337"><a href="http://bigbluebutton.org/blog/">Blog</a></li>
<li id="menu-item-716" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-has-children menu-item-716"><a href="http://bigbluebutton.org/foundation/">License</a>
<ul class="sub-menu">
<li id="menu-item-731" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-731"><a href="http://bigbluebutton.org/trademark/">Trademark</a></li>
<li id="menu-item-3077" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-3077"><a href="http://bigbluebutton.org/open-source-license/">Open Source License</a></li>
</ul>
</li>
<li id="menu-item-1163" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-1163"><a href="http://docs.bigbluebutton.org/">Docs</a></li>
</ul> <!-- /#main-nav -->
</nav>
<div class="social-widget">
<div id="text-6" class="widget widget_text"><strong class="widgettitle">Follow Us</strong> <div class="textwidget"><ul class="links-list">
<li><a href="http://facebook.com/bigbluebutton" title="BigBlueButton Facebook Page" target="_BLANK"><img src="/wp-content/uploads/2011/12/facebook.png" alt="Facebook"></a></li>
<li><a href="https://twitter.com/bigbluebutton" title="BigBlueButton Twitter Page" target="_BLANK"><img src="/wp-content/uploads/2011/12/twitter.png" alt="Twitter"></a></li>
<li><a href="http://www.youtube.com/bigbluebuttonshare" title="BigBlueButton YouTube Page" target="_BLANK"><img src="/wp-content/uploads/2011/12/youtube_icon.png" alt="YouTube"></a></li>
<li><a href="https://plus.google.com/b/108594579970371808676/108594579970371808676/posts" title="BigBlueButton Google Plus Page" target="_BLANK"><img src="/wp-content/uploads/2012/07/googleplus.png" alt="Google Plus"></a></li>
<!-- <li><a href="http://flickr.com" title="BigBlueButton Flickr Page"><img src="/wp-content/uploads/2011/12/flickr.png" alt="Flickr"></a></li> -->
</ul>
</div>
</div>
<div class="rss"><a href="http://bigbluebutton.org/feed/">RSS</a></div>
</div>
<!-- /.social-widget -->
<div class="header-widget">
</div>
<!-- /.header-widget -->
</header>
<!-- /#header -->
<div id="nav-bar">
</div>
<!-- /#nav-bar -->
</div>
<!-- /.headerwrap -->
<div id="body" class="clearfix">
<div id="upperwrap">
<div class="pagewidth">
<h1 class="page-title">Videos</h1>
</div>
<!-- /.page-title -->
</div>
<!-- /#upperwrap -->
<!-- layout-container -->
<div id="layout" class="pagewidth clearfix sidebar-none">
<!-- content -->
<div id="content">
<div class="clearfix"></div>
<h2><strong> Desktop Sharing Tutorial Videos (1.1)</strong></h2>
<p>The following videos give you an overview of using Desktop Sharing in BigBlueButton.</p>
<div class="shortcode col3-1 first"><a class="lightbox" title="Desktop Sharing on Windows using Internet Explorer" href="https://www.youtube.com/watch?v=w0qei2-uDpI" rel="prettyPhoto"><img title="Desktop Sharing on Windows using Internet Explorer" src="/wp-content/uploads/2013/06/p-overview-081.png" alt="Desktop Sharing on Windows using Internet Explorer" width="285"></a><p></p>
<h5>Desktop Sharing on Windows using Internet Explorer</h5>
<p>This video gives you an overview of how to use desktop sharing on Windows OS using Internet Explorer.</p>
<p><a class="shortcode button green lightbox" title="Desktop Sharing on Windows using Internet Explorer" href="https://www.youtube.com/watch?v=w0qei2-uDpI" rel="prettyPhoto">Play Video </a>
</p></div>
<div class="shortcode col3-1"><a class="lightbox" title="Desktop Sharing on Windows using Firefox" href="https://www.youtube.com/watch?v=7ZuunBQbmj8" rel="prettyPhoto"><img title="Desktop Sharing on Windows using Firefox" src="/wp-content/uploads/2013/06/p-overview-081.png" alt="Desktop Sharing on Windows using Firefox" width="285"></a><p></p>
<h5>Desktop Sharing on Windows using Firefox</h5>
<p>This video gives you an overview of how to use desktop sharing on Windows OS using Firefox.</p>
<p><a class="shortcode button green lightbox" title="Desktop Sharing on Windows using Firefox" href="https://www.youtube.com/watch?v=7ZuunBQbmj8" rel="prettyPhoto">Play Video </a>
</p></div>
<div class="shortcode col3-1"><a class="lightbox" title="Desktop Sharing on Windows using Chrome" href="https://www.youtube.com/watch?v=bx9Tupfro_I" rel="prettyPhoto"><img title="Desktop Sharing on Windows using Chrome" src="/wp-content/uploads/2013/06/p-overview-081.png" alt="Desktop Sharing on Windows using Chrome" width="285"></a><p></p>
<h5>Desktop Sharing on Windows using Chrome</h5>
<p>This video gives you an overview of how to use desktop sharing on Windows OS using Chrome.</p>
<p><a class="shortcode button green lightbox" title="Desktop Sharing on Windows using Chrome" href="https://www.youtube.com/watch?v=bx9Tupfro_I" rel="prettyPhoto">Play Video </a>
</p></div>
<div class="shortcode col3-1 first"><a class="lightbox" title="Desktop Sharing on Mac OS X using Chrome" href="https://www.youtube.com/watch?v=Xezw2moXZQE" rel="prettyPhoto"><img title="Desktop Sharing on Mac OS X using Chrome" src="/wp-content/uploads/2013/06/p_student-081.png" alt="Desktop Sharing on Mac OS X using Chrome" width="285"></a><p></p>
<h5>Desktop Sharing on Mac OS X using Chrome</h5>
<p>This video gives you an overview of using desktop sharing on Mac OS X using Chrome.</p>
<p><a class="shortcode button green lightbox" title="Desktop Sharing on Mac OS X using Chrome" href="https://www.youtube.com/watch?v=Xezw2moXZQE" rel="prettyPhoto">Play Video </a>
</p></div>
<div class="shortcode col3-1"><a class="lightbox" title="Desktop Sharing on Mac OS X using Safari" href="https://www.youtube.com/watch?v=SmIMd8abeec" rel="prettyPhoto"><img title="Desktop Sharing on Mac OS X using Safari" src="/wp-content/uploads/2011/12/safari_new.png" alt="Desktop Sharing on Mac OS X using Safari" width="285"></a><p></p>
<h5>Desktop Sharing on Mac OS X using Safari</h5>
<p>This video gives you an overview of using desktop sharing on Mac OS X using Safari.</p>
<p><a class="shortcode button green lightbox" title="Desktop Sharing on Mac OS X using Safari" href="https://www.youtube.com/watch?v=SmIMd8abeec" rel="prettyPhoto">Play Video </a></p>
</div>
<div class="shortcode col3-1"><a class="lightbox" title="Desktop Sharing on Mac OS X using FireFox" href="https://www.youtube.com/watch?v=FmTJOB4SODc" rel="prettyPhoto"><img title="Desktop Sharing on Mac OS X using FireFox" src="/wp-content/uploads/2011/12/macosx-firefox.png" alt="Desktop Sharing on Mac OS X using FireFox" width="285"></a><p></p>
<h5>Desktop Sharing on Mac OS X using FireFox</h5>
<p>This video gives you an overview of using desktop sharing on Mac OS X using FireFox.</p>
<p><a class="shortcode button green lightbox" title="Desktop Sharing on Mac OS X using FireFox" href="https://www.youtube.com/watch?v=FmTJOB4SODc" rel="prettyPhoto">Play Video </a></p>
</div>
<p><!--
<a class="lightbox" title="Viewer Overview" href="http://www.youtube.com/watch?v=4C-rOd8bi6s&amp;width=800&amp;height=600" rel="prettyPhoto"><img title="Viewer Overview" src="/wp-content/uploads/2013/06/dev-08.png" alt="Student Overview" width="285" /></a>
<h5>Developer Overview</h5>
This video gives you an overview of setting up BigBlueButton.
<a class="shortcode button green lightbox" title="Viewer Overview" href="http://www.youtube.com/watch?v=4C-rOd8bi6s&amp;width=800&amp;height=600" rel="prettyPhoto">Play Video </a>
<div id="vid_tut">
<h2><strong> Tutorial Videos (0.8)</strong></h2>
The BigBlueButton community wants to make the process of learning and understanding our open source web conferencing software as easy as possible. Therefore, we have created the following “How-To” videos.
<div class="shortcode col3-1 first"><a class="lightbox" title="BigBlueButton Teacher Overview" href="http://www.youtube.com/watch?v=S4eNl9Afipo?width=800&amp;height=600" rel="prettyPhoto"><img title="Teacher Overview" src="/wp-content/uploads/2013/06/teacher-overview-0.8.png" alt="Teacher Overview" width="285" /></a>
<h5>Teacher Overview</h5>
This video shows you how to present information to remote students using BigBlueButton.
<a class="shortcode button green lightbox" title="BigBlueButton Teacher Overview" href="http://www.youtube.com/watch?v=S4eNl9Afipo" rel="prettyPhoto">Play Video </a>
</div>
<div class="shortcode col3-1"><a class="lightbox" title="BigBlueButton Student Overview" href="http://www.youtube.com/watch?v=U8P9RJDk42M&amp;width=800&amp;height=600" rel="prettyPhoto"><img title="Student Overview" src="/wp-content/uploads/2013/06/student-overview-08.png" alt="Student Overview" width="285" /></a>
<h5>Student Overview</h5>
Student tutorial video for BigBlueButton. Here you will be given an overview of the open source software and a break down of all the individual modules.
<a class="shortcode button green lightbox" title="BigBlueButton Student Overview" href="http://www.youtube.com/watch?v=U8P9RJDk42M&amp;width=800&amp;height=600" rel="prettyPhoto">Play Video </a>
</div>
<div class="shortcode col3-1"><a class="lightbox" title="Viewer Overview" href="http://www.youtube.com/watch?v=4C-rOd8bi6s&amp;width=800&amp;height=600" rel="prettyPhoto"><img title="Viewer Overview" src="/wp-content/uploads/2013/06/dev-08.png" alt="Student Overview" width="285" /></a>
<h5>Developer Overview</h5>
This video gives you an overview of setting up BigBlueButton.
<a class="shortcode button green lightbox" title="Viewer Overview" href="http://www.youtube.com/watch?v=4C-rOd8bi6s&amp;width=800&amp;height=600" rel="prettyPhoto">Play Video </a>
</div>
</div>
--></p>
<div class="clearfix"></div>
<!-- comments -->
<!-- /comments -->
</div>
<!-- /content -->
</div>
<!-- /layout-container -->
</div>
<!-- /body -->
<div class="footer-widgetswrap clearfix">
<div class="footer-widgets pagewidth clearfix">
<div class="col3-1 first">
<section id="text-3" class="widget widget_text"><h4 class="widgettitle">Overview</h4> <div class="textwidget"><ul>
<li class="menu item">
<a href="/home" title="BigBlueButton Home">Home</a>
</li>
<li class="menu item">
<a href="/overview" title="BigBlueButton Overview">Overview</a>
</li>
<li class="menu item">
<a href="/videos" title="BigBlueButton Videos">Videos</a>
</li>
<li class="menu item" title="BigBlueButton Demo">
<a href="http://demo.bigbluebutton.org">Demo</a>
</li>
<li class="menu item">
<a href="/blog">Blog</a>
</li>
<li class="menu item">
<a href="/support" title="BigBlueButton Developer Community">Support</a>
</li>
<li class="menu item">
<a href="http://code.google.com/p/bigbluebutton/" title="Download BigBlueButton">Download</a>
</li>
</ul></div>
</section> </div>
<div class="col3-1 ">
<section id="themify-feature-posts-2" class="widget feature-posts"><h4 class="widgettitle">Recent Posts</h4><ul class="feature-posts-list"><li><a href="http://bigbluebutton.org/2016/05/17/bigbluebutton-1-0-released/" class="feature-posts-title">BigBlueButton 1.0 Released</a> <br><small>May 17, 2016</small> <br><span class="post-excerpt">The BigBlueButton project is pleased to announce the</span></li><li><a href="http://bigbluebutton.org/2016/05/03/bigbluebutton-summit-viii-ottawa-ontario-canada/" class="feature-posts-title">BigBlueButton Summit VIII: Ottawa, Ontario, Canada</a> <br><small>May 03, 2016</small> <br><span class="post-excerpt">The BigBlueButton developer community met in Ottawa,</span></li><li><a href="http://bigbluebutton.org/2016/04/06/agora-learning-integrates-with-bigbluebutton/" class="feature-posts-title">Agora Learning integrates with BigBlueButton</a> <br><small>Apr 06, 2016</small> <br><span class="post-excerpt">Agora Learning, a learning Management System from Tree</span></li><li><a href="http://bigbluebutton.org/2016/01/21/nice-tweet-from-boston-college/" class="feature-posts-title">Nice tweet from Boston College</a> <br><small>Jan 21, 2016</small> <br><span class="post-excerpt">Just saw this tweet from Boston College. Looks like</span></li></ul></section> </div>
<div class="col3-1 ">
<section id="text-12" class="widget widget_text"> <div class="textwidget"><iframe title="Twitter Timeline" data-widget-id="346300284898783232" style="position: static; visibility: visible; display: inline-block; width: 520px; height: 300px; padding: 0px; border: medium none; max-width: 100%; min-width: 180px; margin-top: 0px; margin-bottom: 0px; min-height: 200px;" class="twitter-timeline twitter-timeline-rendered" allowfullscreen="true" allowtransparency="true" scrolling="no" id="twitter-widget-0" frameborder="0"></iframe>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</div>
</section> </div>
</div>
<!-- /.footer-widgets -->
</div>
<!-- /.footer-widgetswrap -->
<div id="footerwrap">
<footer id="footer" class="pagewidth clearfix">
<div id="footer-logo">
<a href="http://bigbluebutton.org/">BigBluebutton</a>
</div>
<!-- /footer-logo -->
<div class="footer-text clearfix">
<!-- div class="one"><center> Copyright © 2014 BigBlueButton Inc </center></div -->
<div class="one"><center> Copyright © 2016 BigBlueButton Inc. </center></div>
<div class="two"> </div>
</div>
<!-- /.footer-text -->
</footer>
<!-- /#footer -->
</div>
<!-- /#footerwrap -->
<!-- /#pagewrap -->
<!-- jquery -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>
<script type="text/javascript">
!window.jQuery && document.write('<script type="text/javascript" src="http://bigbluebutton.org/wp-content/themes/suco/js/jquery.js"><\/script>')
</script>
<!-- prettyphoto -->
<script type="text/javascript" src="http://bigbluebutton.org/wp-content/themes/suco/js/jquery.prettyPhoto.js"></script>
<link rel="stylesheet" href="http://bigbluebutton.org/wp-content/themes/suco/prettyPhoto.css" type="text/css" media="screen">
<script type="text/javascript">
if (screen.width>=480) {
jQuery(function($) {
$("a[rel^='prettyPhoto']").prettyPhoto({ social_tools: false, deeplinking: false });
});
}
</script>
<!-- theme function script -->
<script type="text/javascript" src="http://bigbluebutton.org/wp-content/themes/suco/js/script.js"></script>
<!-- wp_footer -->
<script type="text/javascript">
!window.jQuery && document.write('<script type="text/javascript" src="http://bigbluebutton.org/wp-content/themes/suco/themify/js/jquery.js"><\/script>')
</script>
<!-- slider -->
<script type="text/javascript" src="http://bigbluebutton.org/wp-content/themes/suco/js/slider.js"></script>
<script>
jQuery(document).ready(function($) {
$('#slider').flexslider({
animation: "slide",
slideshow: true,
animationLoop: true,
directionNav: true,
prevText: "&laquo;",
nextText: "&raquo;",
slideshowSpeed: 6000,
pauseOnHover: true
});
});
// expand slider
$('#slider .slides').css('height','auto');
</script>
<script type="text/javascript" src="http://bigbluebutton.org/wp-includes/js/wp-embed.min.js?ver=21e517e0ddb856c019dcb251670c2be4"></script>
<iframe style="position: absolute; visibility: hidden; display: none; width: 0px; height: 0px; padding: 0px; border: medium none;" allowfullscreen="true" allowtransparency="true" scrolling="no" id="rufous-sandbox" frameborder="0"></iframe></body>
<html/>

View File

@ -1,100 +1,97 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.managers
{
import com.asfusion.mate.events.Dispatcher;
import flash.events.TimerEvent;
import flash.utils.Timer;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.modules.screenshare.services.ScreenshareService;
import org.bigbluebutton.modules.screenshare.view.components.ScreensharePublishWindow;
public class PublishWindowManager {
private static const LOGGER:ILogger = getClassLogger(PublishWindowManager);
private var shareWindow:ScreensharePublishWindow;
private var globalDispatcher:Dispatcher;
private var service:ScreenshareService;
private var buttonShownOnToolbar:Boolean = false;
// Timer to auto-publish webcam. We need this timer to delay
// the auto-publishing until after the Viewers's window has loaded
// to receive the publishing events. Otherwise, the user joining next
// won't be able to view the webcam.
private var autoPublishTimer:Timer;
public function PublishWindowManager(service:ScreenshareService) {
LOGGER.debug("PublishWindowManager init");
globalDispatcher = new Dispatcher();
this.service = service;
}
public function stopSharing():void {
if (shareWindow != null) shareWindow.stopSharing();
}
public function startSharing(uri:String, room:String, autoStart:Boolean, autoFullScreen:Boolean):void {
LOGGER.debug("DS:PublishWindowManager::opening desk share window, autostart=" + autoStart + " autoFullScreen=" + autoFullScreen);
shareWindow = new ScreensharePublishWindow();
shareWindow.initWindow(service.getConnection(), uri, room, autoStart, autoFullScreen);
shareWindow.visible = true;
openWindow(shareWindow);
if (autoStart || autoFullScreen) {
/*
* Need to have a timer to trigger auto-publishing of deskshare.
*/
shareWindow.btnFSPublish.enabled = false;
shareWindow.btnRegionPublish.enabled = false;
autoPublishTimer = new Timer(2000, 1);
autoPublishTimer.addEventListener(TimerEvent.TIMER, autopublishTimerHandler);
autoPublishTimer.start();
}
}
private function autopublishTimerHandler(event:TimerEvent):void {
shareWindow.shareScreen(true);
}
public function handleShareWindowCloseEvent():void {
closeWindow(shareWindow);
}
private function openWindow(window:IBbbModuleWindow):void {
var event:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
private function closeWindow(window:IBbbModuleWindow):void {
var event:CloseWindowEvent = new CloseWindowEvent(CloseWindowEvent.CLOSE_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
}
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.managers {
import com.asfusion.mate.events.Dispatcher;
import flash.events.TimerEvent;
import flash.utils.Timer;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.modules.screenshare.services.ScreenshareService;
import org.bigbluebutton.modules.screenshare.view.components.ScreensharePublishWindow;
public class PublishWindowManager {
private static const LOGGER:ILogger = getClassLogger(PublishWindowManager);
private var shareWindow:ScreensharePublishWindow;
private var globalDispatcher:Dispatcher;
private var service:ScreenshareService;
private var buttonShownOnToolbar:Boolean = false;
// Timer to auto-publish webcam. We need this timer to delay
// the auto-publishing until after the Viewers's window has loaded
// to receive the publishing events. Otherwise, the user joining next
// won't be able to view the webcam.
private var autoPublishTimer:Timer;
public function PublishWindowManager(service:ScreenshareService) {
LOGGER.debug("PublishWindowManager init");
globalDispatcher = new Dispatcher();
this.service = service;
}
public function stopSharing():void {
if (shareWindow != null) shareWindow.stopSharing();
}
public function startSharing(uri:String, room:String, autoStart:Boolean, autoFullScreen:Boolean):void {
LOGGER.debug("DS:PublishWindowManager::opening desk share window, autostart=" + autoStart + " autoFullScreen=" + autoFullScreen);
shareWindow = new ScreensharePublishWindow();
shareWindow.initWindow(service.getConnection(), uri, room, autoStart, autoFullScreen);
shareWindow.visible = true;
openWindow(shareWindow);
if (autoStart || autoFullScreen) {
/*
* Need to have a timer to trigger auto-publishing of deskshare.
*/
shareWindow.btnFSPublish.enabled = false;
shareWindow.btnRegionPublish.enabled = false;
autoPublishTimer = new Timer(2000, 1);
autoPublishTimer.addEventListener(TimerEvent.TIMER, autopublishTimerHandler);
autoPublishTimer.start();
}
}
private function autopublishTimerHandler(event:TimerEvent):void {
shareWindow.shareScreen(true);
}
public function handleShareWindowCloseEvent():void {
closeWindow(shareWindow);
}
private function openWindow(window:IBbbModuleWindow):void {
var event:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
private function closeWindow(window:IBbbModuleWindow):void {
var event:CloseWindowEvent = new CloseWindowEvent(CloseWindowEvent.CLOSE_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
}
}

View File

@ -1,267 +1,223 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.managers
{
import com.asfusion.mate.events.Dispatcher;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.modules.screenshare.events.IsSharingScreenEvent;
import org.bigbluebutton.modules.screenshare.events.ShareStartRequestResponseEvent;
import org.bigbluebutton.modules.screenshare.events.StartShareRequestFailedEvent;
import org.bigbluebutton.modules.screenshare.events.StartShareRequestSuccessEvent;
import org.bigbluebutton.modules.screenshare.events.UseJavaModeCommand;
import org.bigbluebutton.modules.screenshare.events.StreamStartedEvent;
import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
import org.bigbluebutton.modules.screenshare.model.ScreenshareModel;
import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
import org.bigbluebutton.modules.screenshare.services.ScreenshareService;
import org.bigbluebutton.modules.screenshare.utils.BrowserCheck;
import org.bigbluebutton.main.api.JSLog;
public class ScreenshareManager {
private static const LOGGER:ILogger = getClassLogger(ScreenshareManager);
private static const LOG:String = "SC::ScreenshareManager - ";
private var publishWindowManager:PublishWindowManager;
private var viewWindowManager:ViewerWindowManager;
private var toolbarButtonManager:ToolbarButtonManager;
private var module:ScreenshareModule;
private var service:ScreenshareService;
private var globalDispatcher:Dispatcher;
private var sharing:Boolean = false;
private var usingJava:Boolean = true;
public function ScreenshareManager() {
JSLog.warn("ScreenshareManager::ScreenshareManager", {});
service = new ScreenshareService();
globalDispatcher = new Dispatcher();
publishWindowManager = new PublishWindowManager(service);
viewWindowManager = new ViewerWindowManager(service);
toolbarButtonManager = new ToolbarButtonManager();
}
public function handleStartModuleEvent(module:ScreenshareModule):void {
JSLog.warn("ScreenshareManager::handleStartModuleEvent", {});
LOGGER.debug("Screenshare Module starting");
this.module = module;
service.handleStartModuleEvent(module);
if (UsersUtil.amIPresenter()) {
initDeskshare();
}
}
public function handleStopModuleEvent():void {
LOGGER.debug("Screenshare Module stopping");
publishWindowManager.stopSharing();
viewWindowManager.stopViewing();
service.disconnect();
}
public function handleConnectionSuccessEvent():void {
LOGGER.debug("handle Connection Success Event");
service.checkIfPresenterIsSharingScreen();
}
public function handleStreamStoppedEvent():void {
LOGGER.debug("Sending deskshare stopped command");
service.stopSharingDesktop(module.getRoom(), module.getRoom());
}
public function handleStreamStartedEvent(event: StreamStartedEvent):void {
ScreenshareModel.getInstance().streamId = event.streamId;
ScreenshareModel.getInstance().width = event.width;
ScreenshareModel.getInstance().height = event.height;
ScreenshareModel.getInstance().url = event.url;
if (UsersUtil.amIPresenter()) {
// var dispatcher:Dispatcher = new Dispatcher();
// dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
} else {
handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height);
}
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
}
public function handleIsSharingScreenEvent(event: IsSharingScreenEvent):void {
ScreenshareModel.getInstance().streamId = event.streamId;
ScreenshareModel.getInstance().width = event.width;
ScreenshareModel.getInstance().height = event.height;
ScreenshareModel.getInstance().url = event.url;
if (UsersUtil.amIPresenter()) {
// var dispatcher:Dispatcher = new Dispatcher();
// dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
} else {
handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height);
}
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
}
public function handleStartedViewingEvent(stream:String):void {
LOGGER.debug("handleStartedViewingEvent [" + stream + "]");
service.sendStartedViewingNotification(stream);
}
private function initDeskshare():void {
JSLog.warn("ScreenshareManager::initDeskshare", {});
sharing = false;
var option:ScreenshareOptions = new ScreenshareOptions();
option.parseOptions();
if (option.autoStart) {
handleStartSharingEvent(true);
}
if(option.showButton){
toolbarButtonManager.addToolbarButton();
}
}
public function handleMadePresenterEvent(e:MadePresenterEvent):void {
LOGGER.debug("Got MadePresenterEvent ");
initDeskshare();
}
public function handleMadeViewerEvent(e:MadePresenterEvent):void{
LOGGER.debug("Got MadeViewerEvent ");
toolbarButtonManager.removeToolbarButton();
if (sharing) {
service.requestStopSharing(ScreenshareModel.getInstance().streamId);
publishWindowManager.stopSharing();
}
sharing = false;
}
public function handleRequestStartSharingEvent():void {
JSLog.warn("ScreenshareManager::handleRequestStartSharingEvent", {});
toolbarButtonManager.startedSharing();
var option:ScreenshareOptions = new ScreenshareOptions();
option.parseOptions();
if (option.useWebRTCIfAvailable && !BrowserCheck.isWebRTCSupported()) {
usingJava = true;
var autoStart:Boolean = false; // harcode for now
publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen);
sharing = true;
service.requestStartSharing();
} else {
usingJava = false;
}
}
public function handleRequestStopSharingEvent():void {
JSLog.warn("ScreenshareManager::handleRequestStopSharingEvent", {});
service.requestStopSharing(ScreenshareModel.getInstance().streamId);
}
public function handleShareStartRequestResponseEvent(event:ShareStartRequestResponseEvent):void {
JSLog.warn("ScreenshareManager::handleShareStartRequestResponseEvent", {});
LOGGER.debug("handleShareStartRequestResponseEvent");
var dispatcher:Dispatcher = new Dispatcher();
if (event.success) {
ScreenshareModel.getInstance().authToken = event.token;
ScreenshareModel.getInstance().jnlp = event.jnlp;
dispatcher.dispatchEvent(new StartShareRequestSuccessEvent(ScreenshareModel.getInstance().authToken));
} else {
dispatcher.dispatchEvent(new StartShareRequestFailedEvent());
}
}
public function handleStartSharingEvent(autoStart:Boolean):void {
JSLog.warn("ScreenshareManager::handleStartSharingEvent", {});
LOGGER.debug("handleStartSharingEvent");
//toolbarButtonManager.disableToolbarButton();
/*toolbarButtonManager.startedSharing();*/
var option:ScreenshareOptions = new ScreenshareOptions();
option.parseOptions();
/**/
/*options.useWebRTCIfAvailable && !BrowserCheck.isWebRTCSupported()*/
publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen);
sharing = true;
// falling back to java, or we can't use WebRTC
/*if (autoStart || (options.useWebRTCIfAvailable && !BrowserCheck.isWebRTCSupported())) {
if (BrowserCheck.isUsingLessThanChrome38OnMac()) {
usingJava = false;
publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen);
} else {
var javaIssue:String = JavaCheck.checkJava();
if (javaIssue != null) {
if (BrowserCheck.isChrome42OrHigher()) {
usingJava = false;
publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen);
} else {
usingJava = false;
publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen);
}
} else {
usingJava = true;
toolbarButtonManager.startedSharing();
publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen);
sharing = true;
}
}
} else {
// using WebRTC or not a fallback case
usingJava = false;
}*/
}
public function handleShareWindowCloseEvent():void {
//toolbarButtonManager.enableToolbarButton();
publishWindowManager.handleShareWindowCloseEvent();
sharing = false;
toolbarButtonManager.stopedSharing();
}
public function handleViewWindowCloseEvent():void {
LOGGER.debug("Received stop viewing command");
viewWindowManager.handleViewWindowCloseEvent();
}
private function handleStreamStartEvent(streamId: String, videoWidth:Number, videoHeight:Number):void{
trace(LOG + "Received start vieweing command");
LOGGER.debug("Received start vieweing command");
if (!usingJava) { return; }
viewWindowManager.startViewing(streamId, videoWidth, videoHeight);
}
public function handleUseJavaModeCommand():void {
JSLog.warn("ScreenshareManager::handleUseJavaModeCommand", {});
usingJava = true;
handleStartSharingEvent(true);
}
public function handleDeskshareToolbarStopEvent():void {
JSLog.warn("ScreenshareManager::handleDeskshareToolbarStopEvent", {});
toolbarButtonManager.stopedSharing();
}
}
}
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.managers {
import com.asfusion.mate.events.Dispatcher;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.modules.screenshare.events.IsSharingScreenEvent;
import org.bigbluebutton.modules.screenshare.events.ShareStartRequestResponseEvent;
import org.bigbluebutton.modules.screenshare.events.StartShareRequestFailedEvent;
import org.bigbluebutton.modules.screenshare.events.StartShareRequestSuccessEvent;
import org.bigbluebutton.modules.screenshare.events.StreamStartedEvent;
import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
import org.bigbluebutton.modules.screenshare.model.ScreenshareModel;
import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
import org.bigbluebutton.modules.screenshare.services.ScreenshareService;
import org.bigbluebutton.modules.screenshare.events.UseJavaModeCommand;
import org.bigbluebutton.modules.screenshare.utils.BrowserCheck;
public class ScreenshareManager {
private static const LOGGER:ILogger = getClassLogger(ScreenshareManager);
private var publishWindowManager:PublishWindowManager;
private var viewWindowManager:ViewerWindowManager;
private var toolbarButtonManager:ToolbarButtonManager;
private var module:ScreenshareModule;
private var service:ScreenshareService;
private var globalDispatcher:Dispatcher;
private var sharing:Boolean = false;
private var usingJava:Boolean = true;
public function ScreenshareManager() {
service = new ScreenshareService();
globalDispatcher = new Dispatcher();
publishWindowManager = new PublishWindowManager(service);
viewWindowManager = new ViewerWindowManager(service);
toolbarButtonManager = new ToolbarButtonManager();
}
public function handleStartModuleEvent(module:ScreenshareModule):void {
LOGGER.debug("Screenshare Module starting");
this.module = module;
service.handleStartModuleEvent(module);
if (UsersUtil.amIPresenter()) {
initDeskshare();
}
}
public function handleStopModuleEvent():void {
LOGGER.debug("Screenshare Module stopping");
publishWindowManager.stopSharing();
viewWindowManager.stopViewing();
service.disconnect();
}
public function handleConnectionSuccessEvent():void {
LOGGER.debug("handle Connection Success Event");
service.checkIfPresenterIsSharingScreen();
}
public function handleStreamStoppedEvent():void {
LOGGER.debug("Sending deskshare stopped command");
service.stopSharingDesktop(module.getRoom(), module.getRoom());
}
public function handleStreamStartedEvent(event:StreamStartedEvent):void {
ScreenshareModel.getInstance().streamId = event.streamId;
ScreenshareModel.getInstance().width = event.width;
ScreenshareModel.getInstance().height = event.height;
ScreenshareModel.getInstance().url = event.url;
if (UsersUtil.amIPresenter()) {
// var dispatcher:Dispatcher = new Dispatcher();
// dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
} else {
handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height);
}
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
}
public function handleIsSharingScreenEvent(event:IsSharingScreenEvent):void {
ScreenshareModel.getInstance().streamId = event.streamId;
ScreenshareModel.getInstance().width = event.width;
ScreenshareModel.getInstance().height = event.height;
ScreenshareModel.getInstance().url = event.url;
if (UsersUtil.amIPresenter()) {
// var dispatcher:Dispatcher = new Dispatcher();
// dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
} else {
handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height);
}
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
}
public function handleStartedViewingEvent(stream:String):void {
LOGGER.debug("handleStartedViewingEvent [" + stream + "]");
service.sendStartedViewingNotification(stream);
}
private function initDeskshare():void {
sharing = false;
var option:ScreenshareOptions = new ScreenshareOptions();
option.parseOptions();
if (option.autoStart) {
handleStartSharingEvent(true);
}
if (option.showButton) {
toolbarButtonManager.addToolbarButton();
}
}
public function handleMadePresenterEvent(e:MadePresenterEvent):void {
LOGGER.debug("Got MadePresenterEvent ");
initDeskshare();
}
public function handleMadeViewerEvent(e:MadePresenterEvent):void {
LOGGER.debug("Got MadeViewerEvent ");
toolbarButtonManager.removeToolbarButton();
if (sharing) {
service.requestStopSharing(ScreenshareModel.getInstance().streamId);
publishWindowManager.stopSharing();
}
sharing = false;
}
public function handleRequestStartSharingEvent():void {
toolbarButtonManager.startedSharing();
var option:ScreenshareOptions = new ScreenshareOptions();
option.parseOptions();
if (option.useWebRTCIfAvailable && !BrowserCheck.isWebRTCSupported()) {
usingJava = true;
var autoStart:Boolean = false; // harcode for now
publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen);
sharing = true;
service.requestStartSharing();
} else {
usingJava = false;
}
}
public function handleRequestStopSharingEvent():void {
service.requestStopSharing(ScreenshareModel.getInstance().streamId);
}
public function handleShareStartRequestResponseEvent(event:ShareStartRequestResponseEvent):void {
LOGGER.debug("handleShareStartRequestResponseEvent");
var dispatcher:Dispatcher = new Dispatcher();
if (event.success) {
ScreenshareModel.getInstance().authToken = event.token;
ScreenshareModel.getInstance().jnlp = event.jnlp;
dispatcher.dispatchEvent(new StartShareRequestSuccessEvent(ScreenshareModel.getInstance().authToken));
} else {
dispatcher.dispatchEvent(new StartShareRequestFailedEvent());
}
}
public function handleStartSharingEvent(autoStart:Boolean):void {
LOGGER.debug("handleStartSharingEvent");
//toolbarButtonManager.disableToolbarButton();
toolbarButtonManager.startedSharing();
var option:ScreenshareOptions = new ScreenshareOptions();
option.parseOptions();
publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen);
sharing = true;
}
public function handleShareWindowCloseEvent():void {
//toolbarButtonManager.enableToolbarButton();
publishWindowManager.handleShareWindowCloseEvent();
sharing = false;
toolbarButtonManager.stopedSharing();
}
public function handleViewWindowCloseEvent():void {
LOGGER.debug("Received stop viewing command");
viewWindowManager.handleViewWindowCloseEvent();
}
private function handleStreamStartEvent(streamId:String, videoWidth:Number, videoHeight:Number):void {
LOGGER.debug("Received start vieweing command");
if (!usingJava) { return; }
viewWindowManager.startViewing(streamId, videoWidth, videoHeight);
}
public function handleUseJavaModeCommand():void {
JSLog.warn("ScreenshareManager::handleUseJavaModeCommand", {});
usingJava = true;
handleStartSharingEvent(true);
}
public function handleDeskshareToolbarStopEvent():void {
JSLog.warn("ScreenshareManager::handleDeskshareToolbarStopEvent", {});
toolbarButtonManager.stopedSharing();
}
}
}

View File

@ -1,82 +1,81 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.managers
{
import com.asfusion.mate.events.Dispatcher;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.modules.screenshare.services.ScreenshareService;
import org.bigbluebutton.modules.screenshare.view.components.ScreenshareViewWindow;
public class ViewerWindowManager {
private static const LOGGER:ILogger = getClassLogger(ViewerWindowManager);
private var viewWindow:ScreenshareViewWindow;
private var service:ScreenshareService;
private var isViewing:Boolean = false;
private var globalDispatcher:Dispatcher;
public function ViewerWindowManager(service:ScreenshareService) {
this.service = service;
globalDispatcher = new Dispatcher();
}
public function stopViewing():void {
if (isViewing) viewWindow.stopViewing();
}
public function handleStartedViewingEvent(stream:String):void{
LOGGER.debug("ViewerWindowManager handleStartedViewingEvent");
service.sendStartedViewingNotification(stream);
}
private function openWindow(window:IBbbModuleWindow):void{
var event:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
public function handleViewWindowCloseEvent():void {
LOGGER.debug("ViewerWindowManager Received stop viewing command");
closeWindow(viewWindow);
isViewing = false;
}
private function closeWindow(window:IBbbModuleWindow):void {
var event:CloseWindowEvent = new CloseWindowEvent(CloseWindowEvent.CLOSE_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
public function startViewing(streamId:String, videoWidth:Number, videoHeight:Number):void{
LOGGER.debug("ViewerWindowManager::startViewing");
viewWindow = new ScreenshareViewWindow();
viewWindow.startVideo(service.getConnection());
openWindow(viewWindow);
isViewing = true;
}
}
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.managers {
import com.asfusion.mate.events.Dispatcher;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.modules.screenshare.services.ScreenshareService;
import org.bigbluebutton.modules.screenshare.view.components.ScreenshareViewWindow;
public class ViewerWindowManager {
private static const LOGGER:ILogger = getClassLogger(ViewerWindowManager);
private var viewWindow:ScreenshareViewWindow;
private var service:ScreenshareService;
private var isViewing:Boolean = false;
private var globalDispatcher:Dispatcher;
public function ViewerWindowManager(service:ScreenshareService) {
this.service = service;
globalDispatcher = new Dispatcher();
}
public function stopViewing():void {
if (isViewing) viewWindow.stopViewing();
}
public function handleStartedViewingEvent(stream:String):void {
LOGGER.debug("ViewerWindowManager handleStartedViewingEvent");
service.sendStartedViewingNotification(stream);
}
private function openWindow(window:IBbbModuleWindow):void {
var event:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
public function handleViewWindowCloseEvent():void {
LOGGER.debug("ViewerWindowManager Received stop viewing command");
closeWindow(viewWindow);
isViewing = false;
}
private function closeWindow(window:IBbbModuleWindow):void {
var event:CloseWindowEvent = new CloseWindowEvent(CloseWindowEvent.CLOSE_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
public function startViewing(streamId:String, videoWidth:Number, videoHeight:Number):void {
LOGGER.debug("ViewerWindowManager::startViewing");
viewWindow = new ScreenshareViewWindow();
viewWindow.startVideo(service.getConnection());
openWindow(viewWindow);
isViewing = true;
}
}
}

View File

@ -1,79 +1,78 @@
package org.bigbluebutton.modules.screenshare.model
{
public class ScreenshareModel
{
private static var instance:ScreenshareModel = null;
private var _isScreenSharing:Boolean = false;
private var _stream: ScreenshareStream = new ScreenshareStream();
public function ScreenshareModel(enforcer:SingletonEnforcer) {
if (enforcer == null){
throw new Error("There can only be 1 ScreenshareModel instance");
}
}
public static function getInstance():ScreenshareModel{
if (instance == null){
instance = new ScreenshareModel(new SingletonEnforcer());
}
return instance;
}
public function get isSharing():Boolean {
return _isScreenSharing;
}
public function get width():int {
return _stream.width;
}
public function set width(w: int):void {
_stream.width = w;
}
public function get height():int {
return _stream.height;
}
public function set height(h: int):void {
_stream.height = h;
}
public function get url():String {
return _stream.url;
}
public function set url(u: String):void {
_stream.url = u;
}
public function get streamId():String {
return _stream.streamId;
}
public function set streamId(s: String):void {
_stream.streamId = s;
}
public function get authToken():String {
return _stream.authToken;
}
public function set authToken(token:String):void {
_stream.authToken = token;
}
public function get jnlp():String {
return _stream.jnlp;
}
public function set jnlp(j:String):void {
_stream.jnlp = j;
}
}
}
class SingletonEnforcer{}
package org.bigbluebutton.modules.screenshare.model {
public class ScreenshareModel {
private static var instance:ScreenshareModel = null;
private var _isScreenSharing:Boolean = false;
private var _stream:ScreenshareStream = new ScreenshareStream();
public function ScreenshareModel(enforcer:SingletonEnforcer) {
if (enforcer == null) {
throw new Error("There can only be 1 ScreenshareModel instance");
}
}
public static function getInstance():ScreenshareModel {
if (instance == null) {
instance = new ScreenshareModel(new SingletonEnforcer());
}
return instance;
}
public function get isSharing():Boolean {
return _isScreenSharing;
}
public function get width():int {
return _stream.width;
}
public function set width(w:int):void {
_stream.width = w;
}
public function get height():int {
return _stream.height;
}
public function set height(h:int):void {
_stream.height = h;
}
public function get url():String {
return _stream.url;
}
public function set url(u:String):void {
_stream.url = u;
}
public function get streamId():String {
return _stream.streamId;
}
public function set streamId(s:String):void {
_stream.streamId = s;
}
public function get authToken():String {
return _stream.authToken;
}
public function set authToken(token:String):void {
_stream.authToken = token;
}
public function get jnlp():String {
return _stream.jnlp;
}
public function set jnlp(j:String):void {
_stream.jnlp = j;
}
}
}
class SingletonEnforcer {
}

View File

@ -1,107 +1,104 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.services
{
import com.asfusion.mate.events.Dispatcher;
import flash.net.NetConnection;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.modules.screenshare.services.red5.Connection;
/**
* The DeskShareProxy communicates with the Red5 deskShare server application
* @author Snap
*
*/
public class ScreenshareService
{
private static const LOG:String = "SC::ScreenshareService - ";
private static const LOGGER:ILogger = getClassLogger(ScreenshareService);
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.services {
import com.asfusion.mate.events.Dispatcher;
private var conn:Connection;
private var module:ScreenshareModule;
private var dispatcher:Dispatcher;
private var uri:String;
private var room:String;
private var sender: MessageSender;
private var receiver: MessageReceiver;
import flash.net.NetConnection;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.modules.screenshare.services.red5.Connection;
public function ScreenshareService() {
this.dispatcher = new Dispatcher();
}
public function handleStartModuleEvent(module:ScreenshareModule):void {
LOGGER.debug("Screenshare Module starting");
this.module = module;
connect(module.uri, module.getRoom());
}
public function connect(uri:String, room:String):void {
this.uri = uri;
this.room = room;
LOGGER.debug("Screenshare Service connecting to " + uri);
conn = new Connection(room);
sender = new MessageSender(conn);
receiver = new MessageReceiver(conn);
conn.setURI(uri);
conn.connect();
}
public function getConnection():NetConnection{
return conn.getConnection();
}
/**
* The DeskShareProxy communicates with the Red5 deskShare server application
* @author Snap
*
*/
public class ScreenshareService {
private static const LOG:String = "SC::ScreenshareService - ";
private static const LOGGER:ILogger = getClassLogger(ScreenshareService);
private var conn:Connection;
private var module:ScreenshareModule;
private var dispatcher:Dispatcher;
private var uri:String;
private var room:String;
private var sender:MessageSender;
private var receiver:MessageReceiver;
public function ScreenshareService() {
this.dispatcher = new Dispatcher();
}
public function handleStartModuleEvent(module:ScreenshareModule):void {
LOGGER.debug("Screenshare Module starting");
this.module = module;
connect(module.uri, module.getRoom());
}
public function connect(uri:String, room:String):void {
this.uri = uri;
this.room = room;
LOGGER.debug("Screenshare Service connecting to " + uri);
conn = new Connection(room);
sender = new MessageSender(conn);
receiver = new MessageReceiver(conn);
conn.setURI(uri);
conn.connect();
}
public function getConnection():NetConnection {
return conn.getConnection();
}
public function disconnect():void {
conn.disconnect();
}
public function checkIfPresenterIsSharingScreen():void {
LOGGER.debug("check if presenter is sharing screen");
sender.isScreenSharing(UsersUtil.getInternalMeetingID());
}
public function requestStartSharing():void {
sender.startShareRequest(UsersUtil.getInternalMeetingID(), UsersUtil.getMyUserID(), UsersUtil.isRecorded());
}
public function requestStopSharing(streamId:String):void {
sender.stopShareRequest(UsersUtil.getInternalMeetingID(), streamId);
}
public function sendStartViewingNotification(captureWidth:Number, captureHeight:Number):void {
conn.sendStartViewingNotification(captureWidth, captureHeight);
}
public function sendStartedViewingNotification(stream:String):void {
conn.sendStartedViewingNotification(stream);
}
public function stopSharingDesktop(meetingId:String, stream:String):void {
conn.stopSharingDesktop(meetingId, stream);
}
public function disconnect():void{
conn.disconnect();
}
public function checkIfPresenterIsSharingScreen():void {
LOGGER.debug("check if presenter is sharing screen");
sender.isScreenSharing(UsersUtil.getInternalMeetingID());
}
public function requestStartSharing():void {
sender.startShareRequest(UsersUtil.getInternalMeetingID(), UsersUtil.getMyUserID(), UsersUtil.isRecorded());
}
public function requestStopSharing(streamId: String):void {
sender.stopShareRequest(UsersUtil.getInternalMeetingID(), streamId);
}
public function sendStartViewingNotification(captureWidth:Number, captureHeight:Number):void{
conn.sendStartViewingNotification(captureWidth, captureHeight);
}
public function sendStartedViewingNotification(stream:String):void{
conn.sendStartedViewingNotification(stream);
}
public function stopSharingDesktop(meetingId: String, stream: String):void {
conn.stopSharingDesktop(meetingId, stream);
}
}
}

View File

@ -1,419 +1,434 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.services.red5
{
import com.asfusion.mate.events.Dispatcher;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
import flash.net.NetConnection;
import flash.net.ObjectEncoding;
import flash.net.Responder;
import flash.net.SharedObject;
import flash.utils.Timer;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.modules.screenshare.events.AppletStartedEvent;
import org.bigbluebutton.modules.screenshare.events.CursorEvent;
import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
public class Connection {
private static const LOGGER:ILogger = getClassLogger(Connection);
package org.bigbluebutton.modules.screenshare.services.red5 {
import com.asfusion.mate.events.Dispatcher;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
import flash.net.NetConnection;
import flash.net.ObjectEncoding;
import flash.net.Responder;
import flash.net.SharedObject;
import flash.utils.Timer;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.modules.screenshare.events.AppletStartedEvent;
import org.bigbluebutton.modules.screenshare.events.CursorEvent;
import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
import org.bigbluebutton.core.managers.ReconnectionManager;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.modules.screenshare.model.ScreenshareModel;
private var netConn:NetConnection;
private var uri:String;
private const connectionTimeout:int = 5000;
private var retryTimer:Timer = null;
private var retryCount:int = 0;
private const MAX_RETRIES:int = 5;
private var deskSO:SharedObject;
private var responder:Responder;
private var width:Number;
private var height:Number;
private var meetingId:String;
private var dispatcher:Dispatcher = new Dispatcher();
private var _messageListeners:Array = new Array();
public function Connection(meetingId:String) {
this.meetingId = meetingId;
}
public function connect(retry:Boolean = false):void {
netConn = new NetConnection();
netConn.proxyType = "best";
netConn.objectEncoding = ObjectEncoding.AMF0;
netConn.client = this;
netConn.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
netConn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
if (getURI().length == 0){
LOGGER.debug("please provide a valid URI connection string. URI Connection String missing");
return;
} else if (netConn.connected){
LOGGER.debug("You are already connected to " + getURI());
return;
}
LOGGER.debug("Trying to connect to [" + getURI() + "] retry=[" + retry + "]");
if (! (retryCount > 0)) {
var ce:ConnectionEvent = new ConnectionEvent(ConnectionEvent.CONNECTING);
dispatcher.dispatchEvent(ce);
}
netConn.connect(getURI());
if (!retry) {
retryTimer = new Timer(connectionTimeout, 1);
retryTimer.addEventListener(TimerEvent.TIMER_COMPLETE, connectTimeoutHandler);
retryTimer.start();
}
}
public function addMessageListener(listener:IMessageListener):void {
_messageListeners.push(listener);
}
public function removeMessageListener(listener:IMessageListener):void {
for (var ob:int=0; ob<_messageListeners.length; ob++) {
if (_messageListeners[ob] == listener) {
_messageListeners.splice (ob,1);
break;
}
}
}
private function notifyListeners(messageName:String, message:Object):void {
if (messageName != null && messageName != "") {
for (var notify:String in _messageListeners) {
_messageListeners[notify].onMessage(messageName, message);
}
} else {
LOGGER.debug("Message name is undefined");
}
}
public function onMessageFromServer(messageName:String, msg:Object):void {
LOGGER.debug("Got message from server [" + messageName + "] data=" + msg.msg);
notifyListeners(messageName, msg);
}
private function connectTimeoutHandler(e:TimerEvent):void {
LOGGER.debug("Connection attempt to [" + getURI() + "] timedout. Retrying.");
retryTimer.stop();
retryTimer = null;
netConn.close();
netConn = null;
var ce:ConnectionEvent;
retryCount++;
if (retryCount < MAX_RETRIES) {
ce = new ConnectionEvent(ConnectionEvent.CONNECTING_RETRY);
ce.retryAttempts = retryCount;
dispatcher.dispatchEvent(ce);
public class Connection {
private static const LOGGER:ILogger = getClassLogger(Connection);
connect(false);
} else {
ce = new ConnectionEvent(ConnectionEvent.CONNECTING_MAX_RETRY);
dispatcher.dispatchEvent(ce);
}
}
public function close():void{
netConn.close();
}
public function isScreenSharing(meetingId: String):void {
var message:Object = new Object();
message["meetingId"] = meetingId;
sendMessage("screenshare.isScreenSharing",
function(result:String):void { // On successful result
LOGGER.debug(result);
},
function(status:String):void { // status - On error occurred
LOGGER.error(status);
},
message
);
}
private function sendMessage(service:String, onSuccess:Function, onFailure:Function, message:Object=null):void {
LOGGER.debug("SENDING [" + service + "]");
var responder:Responder = new Responder(
function(result:Object):void { // On successful result
onSuccess("Successfully sent [" + service + "].");
},
function(status:Object):void { // status - On error occurred
var errorReason:String = "Failed to send [" + service + "]:\n";
for (var x:Object in status) {
errorReason += "\t" + x + " : " + status[x];
}
}
);
if (message == null) {
netConn.call(service, responder);
} else {
netConn.call(service, responder, message);
}
}
public function startShareRequest(meetingId: String, userId: String, record: Boolean):void {
var message:Object = new Object();
message["meetingId"] = meetingId;
message["userId"] = userId;
message["record"] = record;
sendMessage("screenshare.startShareRequest",
function(result:String):void { // On successful result
LOGGER.debug(result);
},
function(status:String):void { // status - On error occurred
LOGGER.error(status);
},
message
);
}
public function stopShareRequest(meetingId: String, streamId: String):void {
var message:Object = new Object();
message["meetingId"] = meetingId;
message["streamId"] = streamId;
sendMessage("screenshare.stopShareRequest",
function(result:String):void { // On successful result
LOGGER.debug(result);
},
function(status:String):void { // status - On error occurred
LOGGER.error(status);
},
message
);
}
public function setURI(p_URI:String):void{
uri = p_URI;
}
public function getURI():String{
return uri + "/" + UsersUtil.getInternalMeetingID();
}
public function onBWCheck(... rest):Number {
return 0;
}
public function onBWDone(... rest):void {
var p_bw:Number;
if (rest.length > 0) p_bw = rest[0];
// your application should do something here
// when the bandwidth check is complete
LOGGER.debug("bandwidth = " + p_bw + " Kbps.");
}
private function sendUserIdToServer():void {
var message:Object = new Object();
message["meetingId"] = meetingId;
message["userId"] = UsersUtil.getMyUserID();
sendMessage("screenshare.setUserId",
function(result:String):void { // On successful result
LOGGER.debug(result);
},
function(status:String):void { // status - On error occurred
LOGGER.error(status);
},
message
);
}
private function netStatusHandler(event:NetStatusEvent):void {
LOGGER.debug("Connected to [" + getURI() + "]. [" + event.info.code + "]");
if (retryTimer) {
retryCount = 0;
LOGGER.debug("Cancelling retry timer.");
retryTimer.stop();
retryTimer = null;
}
var ce:ConnectionEvent;
switch(event.info.code){
case "NetConnection.Connect.Failed":
ce = new ConnectionEvent(ConnectionEvent.FAILED);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.Success":
sendUserIdToServer();
ce = new ConnectionEvent(ConnectionEvent.SUCCESS);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.Rejected":
ce = new ConnectionEvent(ConnectionEvent.REJECTED);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.Closed":
LOGGER.debug("Screenshare connection closed.");
ce = new ConnectionEvent(ConnectionEvent.CLOSED);
break;
case "NetConnection.Connect.InvalidApp":
ce = new ConnectionEvent(ConnectionEvent.INVALIDAPP);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.AppShutdown":
ce = new ConnectionEvent(ConnectionEvent.APPSHUTDOWN);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.NetworkChange":
LOGGER.debug("Detected network change. User might be on a wireless and temporarily dropped connection. Doing nothing. Just making a note.");
break;
default :
// I dispatch DISCONNECTED incase someone just simply wants to know if we're not connected'
// rather than having to subscribe to the events individually
ce = new ConnectionEvent(ConnectionEvent.DISCONNECTED);
dispatcher.dispatchEvent(ce);
break;
}
}
private function securityErrorHandler(event:SecurityErrorEvent):void{
var ce:ConnectionEvent = new ConnectionEvent(ConnectionEvent.SECURITYERROR);
dispatcher.dispatchEvent(ce);
}
public function mouseLocationCallback(x:Number, y:Number):void {
var event:CursorEvent = new CursorEvent(CursorEvent.UPDATE_CURSOR_LOC_EVENT);
event.x = x;
event.y = y;
dispatcher.dispatchEvent(event);
}
public function disconnect():void{
if (netConn != null) netConn.close();
}
private var netConn:NetConnection;
private var uri:String;
private const connectionTimeout:int = 5000;
private var retryTimer:Timer = null;
private var retryCount:int = 0;
private const MAX_RETRIES:int = 5;
private var deskSO:SharedObject;
private var responder:Responder;
private var width:Number;
private var height:Number;
private var meetingId:String;
public function getConnection():NetConnection{
return netConn;
private var dispatcher:Dispatcher = new Dispatcher();
private var _messageListeners:Array = new Array();
private var logoutOnUserCommand:Boolean = false;
private var reconnecting:Boolean = false;
public function Connection(meetingId:String) {
this.meetingId = meetingId;
}
public function connect(retry:Boolean = false):void {
netConn = new NetConnection();
netConn.proxyType = "best";
netConn.objectEncoding = ObjectEncoding.AMF0;
netConn.client = this;
netConn.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
netConn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
if (getURI().length == 0) {
LOGGER.debug("please provide a valid URI connection string. URI Connection String missing");
return;
} else if (netConn.connected) {
LOGGER.debug("You are already connected to " + getURI());
return;
}
LOGGER.debug("Trying to connect to [" + getURI() + "] retry=[" + retry + "]");
if (!(retryCount > 0)) {
var ce:ConnectionEvent = new ConnectionEvent(ConnectionEvent.CONNECTING);
dispatcher.dispatchEvent(ce);
}
netConn.connect(getURI());
if (!retry) {
retryTimer = new Timer(connectionTimeout, 1);
retryTimer.addEventListener(TimerEvent.TIMER_COMPLETE, connectTimeoutHandler);
retryTimer.start();
}
}
public function addMessageListener(listener:IMessageListener):void {
_messageListeners.push(listener);
}
public function removeMessageListener(listener:IMessageListener):void {
for (var ob:int = 0; ob < _messageListeners.length; ob++) {
if (_messageListeners[ob] == listener) {
_messageListeners.splice(ob, 1);
break;
}
}
}
private function notifyListeners(messageName:String, message:Object):void {
if (messageName != null && messageName != "") {
for (var notify:String in _messageListeners) {
_messageListeners[notify].onMessage(messageName, message);
}
} else {
LOGGER.debug("Message name is undefined");
}
}
public function onMessageFromServer(messageName:String, msg:Object):void {
LOGGER.debug("Got message from server [" + messageName + "] data=" + msg.msg);
notifyListeners(messageName, msg);
}
private function connectTimeoutHandler(e:TimerEvent):void {
LOGGER.debug("Connection attempt to [" + getURI() + "] timedout. Retrying.");
retryTimer.stop();
retryTimer = null;
netConn.close();
netConn = null;
var ce:ConnectionEvent;
retryCount++;
if (retryCount < MAX_RETRIES) {
ce = new ConnectionEvent(ConnectionEvent.CONNECTING_RETRY);
ce.retryAttempts = retryCount;
dispatcher.dispatchEvent(ce);
connect(false);
} else {
ce = new ConnectionEvent(ConnectionEvent.CONNECTING_MAX_RETRY);
dispatcher.dispatchEvent(ce);
}
}
public function close():void {
netConn.close();
}
public function isScreenSharing(meetingId:String):void {
var message:Object = new Object();
message["meetingId"] = meetingId;
sendMessage("screenshare.isScreenSharing", function(result:String):void { // On successful result
LOGGER.debug(result);
}, function(status:String):void { // status - On error occurred
LOGGER.error(status);
}, message);
}
private function sendMessage(service:String, onSuccess:Function, onFailure:Function, message:Object = null):void {
LOGGER.debug("SENDING [" + service + "]");
var responder:Responder = new Responder(function(result:Object):void { // On successful result
onSuccess("Successfully sent [" + service + "].");
}, function(status:Object):void { // status - On error occurred
var errorReason:String = "Failed to send [" + service + "]:\n";
for (var x:Object in status) {
errorReason += "\t" + x + " : " + status[x];
}
});
if (message == null) {
netConn.call(service, responder);
} else {
netConn.call(service, responder, message);
}
}
public function startShareRequest(meetingId:String, userId:String, record:Boolean):void {
var message:Object = new Object();
message["meetingId"] = meetingId;
message["userId"] = userId;
message["record"] = record;
sendMessage("screenshare.startShareRequest", function(result:String):void { // On successful result
LOGGER.debug(result);
}, function(status:String):void { // status - On error occurred
LOGGER.error(status);
}, message);
}
public function stopShareRequest(meetingId:String, streamId:String):void {
var message:Object = new Object();
message["meetingId"] = meetingId;
message["streamId"] = streamId;
sendMessage("screenshare.stopShareRequest", function(result:String):void { // On successful result
LOGGER.debug(result);
}, function(status:String):void { // status - On error occurred
LOGGER.error(status);
}, message);
}
public function setURI(p_URI:String):void {
uri = p_URI;
}
public function getURI():String {
return uri + "/" + UsersUtil.getInternalMeetingID();
}
public function onBWCheck(... rest):Number {
return 0;
}
public function onBWDone(... rest):void {
var p_bw:Number;
if (rest.length > 0) p_bw = rest[0];
// your application should do something here
// when the bandwidth check is complete
LOGGER.debug("bandwidth = " + p_bw + " Kbps.");
}
private function sendUserIdToServer():void {
var message:Object = new Object();
message["meetingId"] = meetingId;
message["userId"] = UsersUtil.getMyUserID();
sendMessage("screenshare.setUserId", function(result:String):void { // On successful result
LOGGER.debug(result);
}, function(status:String):void { // status - On error occurred
LOGGER.error(status);
}, message);
}
private function netStatusHandler(event:NetStatusEvent):void {
LOGGER.debug("Connected to [" + getURI() + "]. [" + event.info.code + "]");
if (retryTimer) {
retryCount = 0;
LOGGER.debug("Cancelling retry timer.");
retryTimer.stop();
retryTimer = null;
}
var ce:ConnectionEvent;
switch (event.info.code) {
case "NetConnection.Connect.Failed":
if (reconnecting) {
var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT);
attemptFailedEvent.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
dispatcher.dispatchEvent(attemptFailedEvent);
}
ce = new ConnectionEvent(ConnectionEvent.FAILED);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.Success":
if (reconnecting) {
reconnecting = false;
if (ScreenshareModel.getInstance().isSharing) {
stopShareRequest(UsersUtil.getInternalMeetingID(), ScreenshareModel.getInstance().streamId)
}
var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT);
attemptSucceeded.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
dispatcher.dispatchEvent(attemptSucceeded);
}
sendUserIdToServer();
ce = new ConnectionEvent(ConnectionEvent.SUCCESS);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.Rejected":
ce = new ConnectionEvent(ConnectionEvent.REJECTED);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.Closed":
LOGGER.debug("Screenshare connection closed.");
if (UsersUtil.amIPresenter()) {
// Let's keep our presenter status before disconnected. We can't
// tell the other user's to stop desktop sharing as our connection is broken. (ralam july 24, 2015)
} else {
stopViewing();
}
if (!logoutOnUserCommand) {
reconnecting = true;
var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT);
disconnectedEvent.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
disconnectedEvent.payload.callback = connect;
disconnectedEvent.payload.callbackParameters = [];
dispatcher.dispatchEvent(disconnectedEvent);
}
ce = new ConnectionEvent(ConnectionEvent.CLOSED);
break;
case "NetConnection.Connect.InvalidApp":
ce = new ConnectionEvent(ConnectionEvent.INVALIDAPP);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.AppShutdown":
ce = new ConnectionEvent(ConnectionEvent.APPSHUTDOWN);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.NetworkChange":
LOGGER.debug("Detected network change. User might be on a wireless and temporarily dropped connection. Doing nothing. Just making a note.");
break;
default:
// I dispatch DISCONNECTED incase someone just simply wants to know if we're not connected'
// rather than having to subscribe to the events individually
ce = new ConnectionEvent(ConnectionEvent.DISCONNECTED);
dispatcher.dispatchEvent(ce);
break;
}
}
private function securityErrorHandler(event:SecurityErrorEvent):void {
var ce:ConnectionEvent = new ConnectionEvent(ConnectionEvent.SECURITYERROR);
dispatcher.dispatchEvent(ce);
}
public function mouseLocationCallback(x:Number, y:Number):void {
var event:CursorEvent = new CursorEvent(CursorEvent.UPDATE_CURSOR_LOC_EVENT);
event.x = x;
event.y = y;
dispatcher.dispatchEvent(event);
}
public function disconnect():void {
logoutOnUserCommand = true;
if (netConn != null) netConn.close();
}
public function getConnection():NetConnection {
return netConn;
}
public function connectionFailedHandler(e:ConnectionEvent):void {
LOGGER.error("connection failed to " + uri + " with message " + e.toString());
}
public function connectionRejectedHandler(e:ConnectionEvent):void {
LOGGER.error("connection rejected " + uri + " with message " + e.toString());
}
/**
* Invoked on the server once the clients' applet has started sharing and the server has started a video stream
*
*/
public function appletStarted(videoWidth:Number, videoHeight:Number):void {
LOGGER.debug("Got applet started");
var event:AppletStartedEvent = new AppletStartedEvent();
event.videoWidth = videoWidth;
event.videoHeight = videoHeight;
dispatcher.dispatchEvent(event);
}
/**
* Call this method to send out a room-wide notification to start viewing the stream
*
*/
public function sendStartViewingNotification(captureWidth:Number, captureHeight:Number):void {
try {
deskSO.send("startViewing", captureWidth, captureHeight);
} catch (e:Error) {
LOGGER.error("error while trying to send start viewing notification");
}
}
public function sendStartedViewingNotification(stream:String):void {
LOGGER.debug("Sending start viewing to server");
netConn.call("deskshare.startedToViewStream", null, stream);
}
public function stopSharingDesktop(meetingId:String, stream:String):void {
netConn.call("deskshare.stopSharingDesktop", null, meetingId);
}
/**
* Called by the server when a notification is received to start viewing the broadcast stream .
* This method is called on successful execution of sendStartViewingNotification()
*
*/
public function startViewing(videoWidth:Number, videoHeight:Number):void {
LOGGER.debug("startViewing invoked by server");
var event:ViewStreamEvent = new ViewStreamEvent(ViewStreamEvent.START);
dispatcher.dispatchEvent(event);
}
/**
* Sends a notification through the server to all the participants in the room to stop viewing the stream
*
*/
public function sendStopViewingNotification():void {
LOGGER.debug("Sending stop viewing notification to other clients.");
try {
deskSO.send("stopViewing");
} catch (e:Error) {
LOGGER.debug("could not send stop viewing notification");
}
}
/**
* Called by the server to notify clients that the deskshare stream has stooped.
*/
public function deskshareStreamStopped():void {
stopViewing();
}
/**
* Sends a notification to the module to stop viewing the stream
* This method is called on successful execution of sendStopViewingNotification()
*
*/
public function stopViewing():void {
LOGGER.debug("Received dekskshareStreamStopped");
dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.STOP));
}
}
public function connectionFailedHandler(e:ConnectionEvent):void{
LOGGER.error("connection failed to " + uri + " with message " + e.toString());
}
public function connectionRejectedHandler(e:ConnectionEvent):void{
LOGGER.error("connection rejected " + uri + " with message " + e.toString());
}
/**
* Invoked on the server once the clients' applet has started sharing and the server has started a video stream
*
*/
public function appletStarted(videoWidth:Number, videoHeight:Number):void{
LOGGER.debug("Got applet started");
var event:AppletStartedEvent = new AppletStartedEvent();
event.videoWidth = videoWidth;
event.videoHeight = videoHeight;
dispatcher.dispatchEvent(event);
}
/**
* Call this method to send out a room-wide notification to start viewing the stream
*
*/
public function sendStartViewingNotification(captureWidth:Number, captureHeight:Number):void{
try{
deskSO.send("startViewing", captureWidth, captureHeight);
} catch(e:Error){
LOGGER.error("error while trying to send start viewing notification");
}
}
public function sendStartedViewingNotification(stream:String):void{
LOGGER.debug("Sending start viewing to server");
netConn.call("deskshare.startedToViewStream", null, stream);
}
public function stopSharingDesktop(meetingId: String, stream: String):void {
netConn.call("deskshare.stopSharingDesktop", null, meetingId);
}
/**
* Called by the server when a notification is received to start viewing the broadcast stream .
* This method is called on successful execution of sendStartViewingNotification()
*
*/
public function startViewing(videoWidth:Number, videoHeight:Number):void{
LOGGER.debug("startViewing invoked by server");
var event:ViewStreamEvent = new ViewStreamEvent(ViewStreamEvent.START);
dispatcher.dispatchEvent(event);
}
/**
* Sends a notification through the server to all the participants in the room to stop viewing the stream
*
*/
public function sendStopViewingNotification():void{
LOGGER.debug("Sending stop viewing notification to other clients.");
try{
deskSO.send("stopViewing");
} catch(e:Error){
LOGGER.debug("could not send stop viewing notification");
}
}
/**
* Called by the server to notify clients that the deskshare stream has stooped.
*/
public function deskshareStreamStopped():void {
stopViewing();
}
/**
* Sends a notification to the module to stop viewing the stream
* This method is called on successful execution of sendStopViewingNotification()
*
*/
public function stopViewing():void{
LOGGER.debug("Received dekskshareStreamStopped");
dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.STOP));
}
}
}

View File

@ -41,7 +41,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<mate:Listener type="{LocaleChangeEvent.LOCALE_CHANGED}" method="localeChanged" />
<mate:Listener type="{StopSharingButtonEvent.STOP_SHARING}" method="stopSharingEvent" />
<mate:Listener type="{ShortcutEvent.REMOTE_FOCUS_DESKTOP}" method="remoteFocus" />
<mate:Listener type="{BBBEvent.RECONNECT_DISCONNECTED_EVENT}" method="handleDisconnectedEvent" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
@ -65,7 +66,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
import org.bigbluebutton.core.BBB;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.managers.ReconnectionManager;
private static const LOGGER:ILogger = getClassLogger(ScreensharePublishWindow);
public static const SCALE:Number = 5;
@ -143,18 +145,24 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
return MainCanvas.DESKTOP_SHARING_PUBLISH;
}
public function handleDisconnectedEvent(event:BBBEvent):void {
if (event.payload.type == ReconnectionManager.DESKSHARE_CONNECTION) {
closeWindow();
}
}
/*
* Implement resizeable interface.
*/
*/
public function resetWidthAndHeight():void{/* do nothing */}
public function initWindow(connection:NetConnection, uri:String, room:String, autoStart:Boolean, autoFullScreen:Boolean):void {
this.connection = connection;
this.uri = uri;
this.room = room;
this.room = room;
this.autoStart = autoStart;
/*if(autoFullScreen)
shareScreen(true);*/
shareScreen(true);*/
}
private function handleStartShareRequestSuccessEvent(event:StartShareRequestSuccessEvent):void {
@ -170,7 +178,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
private function startSharing(connection:NetConnection, uri:String, room:String, fullScreen:Boolean):void {
var captureX:Number = 0;
var captureY:Number = 0;
var captureY:Number = 0;
sharingFullScreen = fullScreen;
var authToken:String = ScreenshareModel.getInstance().authToken;
var jnlp: String = ScreenshareModel.getInstance().jnlp;
@ -183,7 +191,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
if (streaming) {
stopStream();
var streamEvent:RequestToStopSharing = new RequestToStopSharing();
dispatchEvent(streamEvent);
dispatchEvent(streamEvent);
}
sharingFullScreen = false;
streaming = false;
@ -195,7 +203,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
if (streaming) {
stopStream();
var streamEvent:StreamEvent = new StreamEvent(StreamEvent.STOP);
dispatchEvent(streamEvent);
dispatchEvent(streamEvent);
}
sharingFullScreen = false;
streaming = false;
@ -266,7 +274,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
ns.play(streamId);
btnClosePublish.enabled = true;
btnFSPublish.enabled = false;
btnFSPublish.enabled = false;
btnRegionPublish.enabled = false;
}
@ -305,9 +313,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
closeWindow();
}
private function closeWindow():void {
dispatchEvent(new ShareWindowEvent(ShareWindowEvent.CLOSE));
}
private function closeWindow():void {
dispatchEvent(new ShareWindowEvent(ShareWindowEvent.CLOSE));
}
private function restartJava():void {
shareScreen(sharingFullScreen);

View File

@ -1,354 +1,367 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-->
<MDIWindow xmlns="flexlib.mdi.containers.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
width="600" height="400"
initialize="init()"
creationComplete="onCreationComplete()"
implements="org.bigbluebutton.common.IBbbModuleWindow"
xmlns:mate="http://mate.asfusion.com/"
title="{ResourceUtil.getInstance().getString('bbb.screenshareView.title')}"
showCloseButton="false"
resize="fitToWindow()" >
<mate:Listener type="{ViewStreamEvent.STOP}" method="onStopViewStreamEvent" />
<mate:Listener type="{LocaleChangeEvent.LOCALE_CHANGED}" method="localeChanged" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flexlib.mdi.events.MDIWindowEvent;
import mx.core.UIComponent;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.events.LocaleChangeEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.api.JSLog;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.screenshare.events.CursorEvent;
import org.bigbluebutton.modules.screenshare.events.StartedViewingEvent;
import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
import org.bigbluebutton.modules.screenshare.events.ViewWindowEvent;
import org.bigbluebutton.modules.screenshare.model.ScreenshareModel;
import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
private static const LOG:String = "SC::ScreenshareViewWIndow - ";
private static const LOGGER:ILogger = getClassLogger(ScreenshareViewWindow);
private var screenHeight:Number = Capabilities.screenResolutionY;
private var screenWidth:Number = Capabilities.screenResolutionX;
private var images:Images = new Images();
[Bindable] public var fitToWidthIcon:Class = images.magnifier;
[Bindable] public var fitToActualSizeIcon:Class = images.mag_reset;
private var streamAvailable:Boolean = false;
private var video:Video;
private var ns:NetStream;
private var videoHolder:UIComponent = new UIComponent();
private var streamId:String;
private var videoHeight:Number = 1;
private var videoWidth:Number = 1;
private static const VIDEO_WIDTH_PADDING:int = 7;
private static const VIDEO_HEIGHT_PADDING:int = 65;
// The following code block is to deal with a bug in FLexLib
// with MDI windows not responding well to being maximized
private var savedWindowWidth:Number;
private var savedWindowHeight:Number;
private var savedX:Number;
private var savedY:Number;
private var isMaximized:Boolean = false;
private var connection:NetConnection;
[Bindable] private var baseIndex:int;
[Bindable] private var dsOptions:ScreenshareOptions;
private function init():void{
dsOptions = new ScreenshareOptions();
baseIndex = dsOptions.baseTabIndex;
}
private function onCreationComplete():void{
viewScreenshareStream();
videoHolder.addChild(video);
this.addChild(videoHolder);
videoHolder.percentWidth = 100;
videoHolder.percentHeight = 100;
addEventListener(MDIWindowEvent.RESIZE_END, onResizeEndEvent);
fitToActualSize();
maximize();
resourcesChanged();
titleBarOverlay.tabIndex = baseIndex;
minimizeBtn.tabIndex = baseIndex+1;
maximizeRestoreBtn.tabIndex = baseIndex+2;
closeBtn.tabIndex = baseIndex+3;
var logData:Object = new Object();
logData.width = videoWidth;
logData.height = videoHeight;
logData.streamId = streamId;
JSLog.debug(LOG + "onCreationComplete", logData);
}
private function onResizeEndEvent(event:MDIWindowEvent):void {
if (event.window == this && streamAvailable) {
fitToWindow();
}
}
public function startVideo(connection:NetConnection):void{
var logData:Object = new Object();
logData.width = videoWidth;
logData.height = videoHeight;
logData.streamId = streamId;
JSLog.debug(LOG + "startVideo", logData);
this.connection = connection;
}
private function viewScreenshareStream():void{
videoWidth = ScreenshareModel.getInstance().width;
videoHeight = ScreenshareModel.getInstance().height;
streamId = ScreenshareModel.getInstance().streamId;
var logData:Object = new Object();
logData.width = videoWidth;
logData.height = videoHeight;
logData.streamId = streamId;
JSLog.debug(LOG + "viewScreenshareStream Chrome", logData);
ns = new NetStream(connection);
ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
ns.client = this;
ns.bufferTime = 0;
ns.receiveVideo(true);
ns.receiveAudio(false);
video = new Video(videoWidth, videoHeight);
video.width = videoWidth;
video.height = videoHeight;
video.smoothing = true;
video.attachNetStream(ns);
ns.play(streamId);
this.title = "Viewing Remote Desktop";
streamAvailable = true;
fitToWindow();
}
public function onMetaData(info:Object):void{
trace("metadata: width=" + info.width + " height=" + info.height);
var logData:Object = new Object();
logData.width = info.width;
logData.height = info.height;
JSLog.debug(LOG + "onMetaData", logData);
}
protected function updateButtonsPosition():void {
if (this.width < bottomBar.width) {
bottomBar.visible = false;
}
if (bottomBar.visible == false) {
bottomBar.y = bottomBar.x = 0;
} else {
bottomBar.y = (this.height - bottomBar.height) / 2;
bottomBar.x = (this.width - bottomBar.width) / 2;
}
}
public function stopViewing():void {
ns.close();
closeWindow();
}
private function onStopViewStreamEvent(event:ViewStreamEvent):void {
stopViewing();
}
private function onAsyncError(e:AsyncErrorEvent):void{
LOGGER.debug("asyncerror " + e.toString());
var logData:Object = new Object();
logData.error = e.toString();
JSLog.debug(LOG + "asyncerror ", logData);
}
private function onNetStatus(e:NetStatusEvent):void{
var logData:Object = new Object();
logData.stream = streamId;
switch(e.info.code){
case "NetStream.Play.Start":
LOGGER.debug("NetStream.Publish.Start for broadcast stream " + streamId);
JSLog.debug(LOG + "NetStream.Publish.Start for broadcast stream ", logData);
var dispatcher: Dispatcher = new Dispatcher();
var viewEvent:StartedViewingEvent = new StartedViewingEvent(StartedViewingEvent.STARTED_VIEWING_EVENT);
viewEvent.stream = streamId;
dispatcher.dispatchEvent(viewEvent);
break;
case "NetStream.Play.UnpublishNotify":
LOGGER.debug("NetStream.Play.UnpublishNotify for broadcast stream " + streamId);
JSLog.debug(LOG + "NetStream.Play.UnpublishNotify for broadcast stream ", logData);
stopViewing();
break;
}
}
public function getPrefferedPosition():String{
return MainCanvas.DESKTOP_SHARING_VIEW;
}
/**
* resizes the desktop sharing video to fit to this window
*/
private function fitToWindow():void{
if (!streamAvailable) return;
if (videoIsSmallerThanWindow()) {
fitWindowToVideo();
}
// Ignore if we are displaying the actual size of the video
if (! btnActualSize.selected) {
fitVideoToWindow();
}
}
private function fitVideoToWindow():void {
if (this.width < this.height) {
fitToWidthAndAdjustHeightToMaintainAspectRatio();
} else {
fitToHeightAndAdjustWidthToMaintainAspectRatio();
}
}
private function fitWindowToVideo():void {
video.width = videoWidth;
videoHolder.width = videoWidth;
video.height = videoHeight;
videoHolder.height = videoHeight;
this.height = videoHeight + VIDEO_HEIGHT_PADDING;
this.width = videoWidth + VIDEO_WIDTH_PADDING;
}
private function videoIsSmallerThanWindow():Boolean {
return (videoHeight < this.height) && (videoWidth < this.width);
}
private function fitToWidthAndAdjustHeightToMaintainAspectRatio():void {
video.width = this.width - VIDEO_WIDTH_PADDING;
videoHolder.width = video.width;
// Maintain aspect-ratio
video.height = (videoHeight * video.width) / videoWidth;
videoHolder.height = video.height;
this.height = video.height + VIDEO_HEIGHT_PADDING;
}
private function fitToHeightAndAdjustWidthToMaintainAspectRatio():void {
video.height = this.height - VIDEO_HEIGHT_PADDING;
videoHolder.height = video.height;
// Maintain aspect-ratio
video.width = (videoWidth * video.height) / videoHeight;
videoHolder.width = video.width;
this.width = video.width + VIDEO_WIDTH_PADDING;
}
/**
* resizes the desktop sharing video to actual video resolution
*/
private function fitToActualSize():void{
if (videoIsSmallerThanWindow()) {
fitWindowToVideo();
} else {
video.width = videoWidth;
videoHolder.width = videoWidth;
video.height = videoHeight;
videoHolder.height = videoHeight;
}
}
private function determineHowToDisplayVideo():void {
if (btnActualSize.selected) {
fitToActualSize();
btnActualSize.toolTip = ResourceUtil.getInstance().getString('bbb.screenshareView.fitToWindow');
btnActualSize.label = ResourceUtil.getInstance().getString('bbb.screenshareView.fitToWindow');
} else {
fitToWindow();
btnActualSize.toolTip = ResourceUtil.getInstance().getString('bbb.screenshareView.actualSize');
btnActualSize.label = ResourceUtil.getInstance().getString('bbb.screenshareView.actualSize');
}
}
private function closeWindow():void {
dispatchEvent(new ViewWindowEvent(ViewWindowEvent.CLOSE));
}
override protected function resourcesChanged():void{
super.resourcesChanged();
this.title = ResourceUtil.getInstance().getString('bbb.screenshareView.title');
if (windowControls != null) {
minimizeBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.minimizeBtn.toolTip");
minimizeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.screenshareView.minimizeBtn.accessibilityName");
maximizeRestoreBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.maximizeRestoreBtn.toolTip");
maximizeRestoreBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.screenshareView.maximizeRestoreBtn.accessibilityName");
closeBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.closeBtn.toolTip");
closeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.screenshareView.closeBtn.accessibilityName");
}
}
private function localeChanged(e:Event):void{
resourcesChanged();
}
]]>
</mx:Script>
<mx:HBox id="bottomBar" visible="true" height="30" horizontalAlign="center" paddingTop="0" paddingBottom="0" width="100%" >
<mx:Button id="btnActualSize" paddingTop="0" paddingBottom="0" styleName="deskshareControlButtonStyle"
toggle="true"
click="determineHowToDisplayVideo()"
selected="false"
height="90%"
label="{btnActualSize.selected ? ResourceUtil.getInstance().getString('bbb.screenshareView.fitToWindow') : ResourceUtil.getInstance().getString('bbb.screenshareView.actualSize')}"
toolTip="{btnActualSize.selected ? ResourceUtil.getInstance().getString('bbb.screenshareView.fitToWindow') : ResourceUtil.getInstance().getString('bbb.screenshareView.actualSize')}"
tabIndex="{baseIndex+4}"/>
</mx:HBox>
</MDIWindow>
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-->
<MDIWindow xmlns="flexlib.mdi.containers.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
width="600"
height="400"
initialize="init()"
creationComplete="onCreationComplete()"
implements="org.bigbluebutton.common.IBbbModuleWindow"
xmlns:mate="http://mate.asfusion.com/"
title="{ ResourceUtil.getInstance().getString('bbb.screenshareView.title') }"
showCloseButton="false"
resize="fitToWindow()">
<mate:Listener type="{ ViewStreamEvent.STOP }" method="onStopViewStreamEvent" />
<mate:Listener type="{ LocaleChangeEvent.LOCALE_CHANGED }" method="localeChanged" />
<mate:Listener type="{ BBBEvent.RECONNECT_DISCONNECTED_EVENT }" method="handleDisconnectedEvent" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flexlib.mdi.events.MDIWindowEvent;
import mx.core.UIComponent;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.events.LocaleChangeEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.api.JSLog;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.screenshare.events.CursorEvent;
import org.bigbluebutton.modules.screenshare.events.StartedViewingEvent;
import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
import org.bigbluebutton.modules.screenshare.events.ViewWindowEvent;
import org.bigbluebutton.modules.screenshare.model.ScreenshareModel;
import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.managers.ReconnectionManager;
import org.bigbluebutton.main.events.BBBEvent;
private static const LOG:String = "SC::ScreenshareViewWIndow - ";
private static const LOGGER:ILogger = getClassLogger(ScreenshareViewWindow);
private var screenHeight:Number = Capabilities.screenResolutionY;
private var screenWidth:Number = Capabilities.screenResolutionX;
private var images:Images = new Images();
[Bindable] public var fitToWidthIcon:Class = images.magnifier;
[Bindable] public var fitToActualSizeIcon:Class = images.mag_reset;
private var streamAvailable:Boolean = false;
private var video:Video;
private var ns:NetStream;
private var videoHolder:UIComponent = new UIComponent();
private var streamId:String;
private var videoHeight:Number = 1;
private var videoWidth:Number = 1;
private static const VIDEO_WIDTH_PADDING:int = 7;
private static const VIDEO_HEIGHT_PADDING:int = 65;
// The following code block is to deal with a bug in FLexLib
// with MDI windows not responding well to being maximized
private var savedWindowWidth:Number;
private var savedWindowHeight:Number;
private var savedX:Number;
private var savedY:Number;
private var isMaximized:Boolean = false;
private var connection:NetConnection;
[Bindable] private var baseIndex:int;
[Bindable] private var dsOptions:ScreenshareOptions;
private function init():void{
dsOptions = new ScreenshareOptions();
baseIndex = dsOptions.baseTabIndex;
}
private function onCreationComplete():void{
viewScreenshareStream();
videoHolder.addChild(video);
this.addChild(videoHolder);
videoHolder.percentWidth = 100;
videoHolder.percentHeight = 100;
addEventListener(MDIWindowEvent.RESIZE_END, onResizeEndEvent);
fitToActualSize();
maximize();
resourcesChanged();
titleBarOverlay.tabIndex = baseIndex;
minimizeBtn.tabIndex = baseIndex+1;
maximizeRestoreBtn.tabIndex = baseIndex+2;
closeBtn.tabIndex = baseIndex+3;
var logData:Object = new Object();
logData.width = videoWidth;
logData.height = videoHeight;
logData.streamId = streamId;
JSLog.debug(LOG + "onCreationComplete", logData);
}
private function onResizeEndEvent(event:MDIWindowEvent):void {
if (event.window == this && streamAvailable) {
fitToWindow();
}
}
public function startVideo(connection:NetConnection):void{
var logData:Object = new Object();
logData.width = videoWidth;
logData.height = videoHeight;
logData.streamId = streamId;
JSLog.debug(LOG + "startVideo", logData);
this.connection = connection;
}
private function viewScreenshareStream():void{
videoWidth = ScreenshareModel.getInstance().width;
videoHeight = ScreenshareModel.getInstance().height;
streamId = ScreenshareModel.getInstance().streamId;
var logData:Object = new Object();
logData.width = videoWidth;
logData.height = videoHeight;
logData.streamId = streamId;
JSLog.debug(LOG + "viewScreenshareStream Chrome", logData);
ns = new NetStream(connection);
ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
ns.client = this;
ns.bufferTime = 0;
ns.receiveVideo(true);
ns.receiveAudio(false);
video = new Video(videoWidth, videoHeight);
video.width = videoWidth;
video.height = videoHeight;
video.smoothing = true;
video.attachNetStream(ns);
ns.play(streamId);
this.title = "Viewing Remote Desktop";
streamAvailable = true;
fitToWindow();
}
public function onMetaData(info:Object):void{
trace("metadata: width=" + info.width + " height=" + info.height);
var logData:Object = new Object();
logData.width = info.width;
logData.height = info.height;
JSLog.debug(LOG + "onMetaData", logData);
}
protected function updateButtonsPosition():void {
if (this.width < bottomBar.width) {
bottomBar.visible = false;
}
if (bottomBar.visible == false) {
bottomBar.y = bottomBar.x = 0;
} else {
bottomBar.y = (this.height - bottomBar.height) / 2;
bottomBar.x = (this.width - bottomBar.width) / 2;
}
}
public function stopViewing():void {
ns.close();
closeWindow();
}
private function onStopViewStreamEvent(event:ViewStreamEvent):void {
stopViewing();
}
private function onAsyncError(e:AsyncErrorEvent):void{
LOGGER.debug("asyncerror " + e.toString());
var logData:Object = new Object();
logData.error = e.toString();
JSLog.debug(LOG + "asyncerror ", logData);
}
private function onNetStatus(e:NetStatusEvent):void{
var logData:Object = new Object();
logData.stream = streamId;
switch(e.info.code){
case "NetStream.Play.Start":
LOGGER.debug("NetStream.Publish.Start for broadcast stream " + streamId);
JSLog.debug(LOG + "NetStream.Publish.Start for broadcast stream ", logData);
var dispatcher: Dispatcher = new Dispatcher();
var viewEvent:StartedViewingEvent = new StartedViewingEvent(StartedViewingEvent.STARTED_VIEWING_EVENT);
viewEvent.stream = streamId;
dispatcher.dispatchEvent(viewEvent);
break;
case "NetStream.Play.UnpublishNotify":
LOGGER.debug("NetStream.Play.UnpublishNotify for broadcast stream " + streamId);
JSLog.debug(LOG + "NetStream.Play.UnpublishNotify for broadcast stream ", logData);
stopViewing();
break;
}
}
public function getPrefferedPosition():String{
return MainCanvas.DESKTOP_SHARING_VIEW;
}
/**
* resizes the desktop sharing video to fit to this window
*/
private function fitToWindow():void{
if (!streamAvailable) return;
if (videoIsSmallerThanWindow()) {
fitWindowToVideo();
}
// Ignore if we are displaying the actual size of the video
if (! btnActualSize.selected) {
fitVideoToWindow();
}
}
private function fitVideoToWindow():void {
if (this.width < this.height) {
fitToWidthAndAdjustHeightToMaintainAspectRatio();
} else {
fitToHeightAndAdjustWidthToMaintainAspectRatio();
}
}
private function fitWindowToVideo():void {
video.width = videoWidth;
videoHolder.width = videoWidth;
video.height = videoHeight;
videoHolder.height = videoHeight;
this.height = videoHeight + VIDEO_HEIGHT_PADDING;
this.width = videoWidth + VIDEO_WIDTH_PADDING;
}
private function videoIsSmallerThanWindow():Boolean {
return (videoHeight < this.height) && (videoWidth < this.width);
}
private function fitToWidthAndAdjustHeightToMaintainAspectRatio():void {
video.width = this.width - VIDEO_WIDTH_PADDING;
videoHolder.width = video.width;
// Maintain aspect-ratio
video.height = (videoHeight * video.width) / videoWidth;
videoHolder.height = video.height;
this.height = video.height + VIDEO_HEIGHT_PADDING;
}
private function fitToHeightAndAdjustWidthToMaintainAspectRatio():void {
video.height = this.height - VIDEO_HEIGHT_PADDING;
videoHolder.height = video.height;
// Maintain aspect-ratio
video.width = (videoWidth * video.height) / videoHeight;
videoHolder.width = video.width;
this.width = video.width + VIDEO_WIDTH_PADDING;
}
/**
* resizes the desktop sharing video to actual video resolution
*/
private function fitToActualSize():void{
if (videoIsSmallerThanWindow()) {
fitWindowToVideo();
} else {
video.width = videoWidth;
videoHolder.width = videoWidth;
video.height = videoHeight;
videoHolder.height = videoHeight;
}
}
private function determineHowToDisplayVideo():void {
if (btnActualSize.selected) {
fitToActualSize();
btnActualSize.toolTip = ResourceUtil.getInstance().getString('bbb.screenshareView.fitToWindow');
btnActualSize.label = ResourceUtil.getInstance().getString('bbb.screenshareView.fitToWindow');
} else {
fitToWindow();
btnActualSize.toolTip = ResourceUtil.getInstance().getString('bbb.screenshareView.actualSize');
btnActualSize.label = ResourceUtil.getInstance().getString('bbb.screenshareView.actualSize');
}
}
private function closeWindow():void {
dispatchEvent(new ViewWindowEvent(ViewWindowEvent.CLOSE));
}
override protected function resourcesChanged():void{
super.resourcesChanged();
this.title = ResourceUtil.getInstance().getString('bbb.screenshareView.title');
if (windowControls != null) {
minimizeBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.minimizeBtn.toolTip");
minimizeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.screenshareView.minimizeBtn.accessibilityName");
maximizeRestoreBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.maximizeRestoreBtn.toolTip");
maximizeRestoreBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.screenshareView.maximizeRestoreBtn.accessibilityName");
closeBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.closeBtn.toolTip");
closeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.screenshareView.closeBtn.accessibilityName");
}
}
private function localeChanged(e:Event):void{
resourcesChanged();
}
public function handleDisconnectedEvent(event:BBBEvent):void {
if (event.payload.type == ReconnectionManager.DESKSHARE_CONNECTION) {
closeWindow();
}
}
]]>
</mx:Script>
<mx:HBox id="bottomBar" visible="true" height="30" horizontalAlign="center" paddingTop="0" paddingBottom="0" width="100%">
<mx:Button id="btnActualSize"
paddingTop="0"
paddingBottom="0"
styleName="deskshareControlButtonStyle"
toggle="true"
click="determineHowToDisplayVideo()"
selected="false"
height="90%"
label="{ btnActualSize.selected ? ResourceUtil.getInstance().getString('bbb.screenshareView.fitToWindow') : ResourceUtil.getInstance().getString('bbb.screenshareView.actualSize') }"
toolTip="{ btnActualSize.selected ? ResourceUtil.getInstance().getString('bbb.screenshareView.fitToWindow') : ResourceUtil.getInstance().getString('bbb.screenshareView.actualSize') }"
tabIndex="{ baseIndex + 4 }" />
</mx:HBox>
</MDIWindow>

View File

@ -281,7 +281,7 @@ uncomment () {
stop_bigbluebutton () {
echo "Stopping BigBlueButton"
if command -v systemctl >/dev/null; then
systemctl stop red5 tomcat7 nginx freeswitch bbb-apps-akka bbb-fsesl-akka
systemctl stop red5 tomcat7 nginx freeswitch bbb-apps-akka bbb-fsesl-akka bbb-record-core.service bbb-record-core.timer
else
/etc/init.d/monit stop
@ -324,7 +324,7 @@ stop_bigbluebutton () {
start_bigbluebutton () {
echo "Starting BigBlueButton"
if command -v systemctl >/dev/null; then
systemctl start red5 tomcat7 nginx freeswitch bbb-apps-akka bbb-fsesl-akka
systemctl start red5 tomcat7 nginx freeswitch bbb-apps-akka bbb-fsesl-akka bbb-record-core.timer
else
$FREESWITCH_INIT_D start
@ -580,10 +580,10 @@ while [ $# -gt 0 ]; do
SALT="${2}"
if [ -z "$SALT" ]; then
BBB_WEB_URL=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}')
SALT=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | grep securitySalt | cut -d= -f2);
SECRET=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | grep securitySalt | cut -d= -f2);
echo
echo " URL: $BBB_WEB_URL/bigbluebutton/"
echo " Salt: $SALT"
echo " Secret: $SECRET"
echo
exit 0
fi
@ -646,11 +646,10 @@ if [ $SALT ]; then
fi
if [ -f /usr/share/bbb-apps-akka/conf/application.conf ]; then
sed -i "s/sharedSecret.[ ]*=[ ]*\"[^\"]*\"/sharedSecret.=\"$SALT\"/g" \
sed -i "s/sharedSecret[ ]*=[ ]*\"[^\"]*\"/sharedSecret=\"$SALT\"/g" \
/usr/share/bbb-apps-akka/conf/application.conf
fi
change_var_salt ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties securitySalt $SALT
echo "Changed BigBlueButton's shared secret to $SALT"
echo
fi
@ -891,6 +890,14 @@ check_configuration() {
echo "# /var/bigbluebutton"
echo "# is not owned by $TOMCAT_USER"
fi
if [ $PROTOCOL_HTTP == "https" ]; then
if ! grep jnlpUrl /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties | grep -q https; then
echo "# Warning: Detected the value for jnlpUrl is not configured for HTTPS"
echo "# /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties"
echo "#"
fi
fi
}
@ -1686,11 +1693,10 @@ if [ -n "$HOST" ]; then
/usr/share/bbb-apps-akka/conf/application.conf
sed -i "s/defaultPresentationURL[ ]*=[ ]*\"[^\"]*\"/defaultPresentationURL=\"${PROTOCOL_HTTP}:\/\/$HOST\/default.pdf\"/g" \
/usr/share/bbb-apps-akka/conf/application.conf
if grep sharedSecret /usr/share/bbb-apps-akka/conf/application.conf | grep -q \"changeme\"; then
SECRET=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | grep securitySalt | cut -d= -f2);
sed -i "s/sharedSecret[ ]*=[ ]*\"[^\"]*\"/sharedSecret=\"$SECRET\"/g" \
/usr/share/bbb-apps-akka/conf/application.conf
fi
# XXX Temporary fix to ensure application.conf has the latest shared secret
SECRET=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | grep securitySalt | cut -d= -f2);
sed -i "s/sharedSecret[ ]*=[ ]*\"[^\"]*\"/sharedSecret=\"$SECRET\"/g" \
/usr/share/bbb-apps-akka/conf/application.conf
fi
#

View File

@ -201,15 +201,15 @@ eventEmitter.on('presenter_assigned_message', function (arg) {
});
const handleRemoveUserEvent = function (arg) {
if (arg.payload.user != null &&
arg.payload.user.userid != null &&
arg.payload.meeting_id != null) {
const meetingId = arg.payload.meeting_id;
const userId = arg.payload.user.userid;
return markUserOffline(meetingId, userId, arg.callback);
const { payload, callback } = arg;
if ((payload.userid || payload.user.userid) &&
payload.meeting_id) {
const meetingId = payload.meeting_id;
const userId = payload.userid || payload.user.userid;
return markUserOffline(meetingId, userId, callback);
} else {
logger.info('could not perform handleRemoveUserEvent');
return arg.callback();
return callback();
}
};

View File

@ -9,5 +9,6 @@
"app.chat.titlePublic": "Public Chat",
"app.chat.titlePrivate": "Private Chat with {name}",
"app.chat.partnerDisconnected": "{name} has left the meeting",
"app.chat.moreMessages": "More messages below"
"app.chat.moreMessages": "More messages below",
"app.kickMessage": "You have been kicked out of the meeting"
}

View File

@ -9,5 +9,6 @@
"app.chat.titlePublic": "Conversa Publíca",
"app.chat.titlePrivate": "Conversa Privada com {name}",
"app.chat.partnerDisconnected": "{name} saiu da sala",
"app.chat.moreMessages": "Mais mensagens abaixo"
"app.chat.moreMessages": "Mais mensagens abaixo",
"app.kickMessage": "Você foi expulso da apresentação"
}

View File

@ -8,7 +8,6 @@ import { subscribeToCollections, setCredentials } from '/imports/ui/components/a
import ChatContainer from '/imports/ui/components/chat/container';
import UserListContainer from '/imports/ui/components/user-list/container';
import Loader from '/imports/ui/components/loader/component';
const browserHistory = useRouterHistory(createHistory)({
basename: '/html5client',

View File

@ -1,5 +1,8 @@
import React, { Component, PropTypes } from 'react';
import Loader from '../loader/component';
import { FormattedMessage } from 'react-intl';
import LoadingScreen from '../loading-screen/component';
import KickedScreen from '../kicked-screen/component';
import Button from '../button/component';
import styles from './styles';
const propTypes = {
@ -117,8 +120,24 @@ export default class App extends Component {
}
render() {
if (this.props.wasKicked) {
return (
<KickedScreen>
<FormattedMessage
id="app.kickMessage"
description="Message when the user is kicked out of the meeting"
defaultMessage="You have been kicked out of the meeting"
/>
<br/><br/>
<Button
label={'OK'}
onClick={this.props.redirectToLogoutUrl}/>
</KickedScreen>
);
}
if (this.props.isLoading) {
return <Loader/>;
return <LoadingScreen/>;
}
return (

View File

@ -1,8 +1,7 @@
import React, { Component, PropTypes, cloneElement } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import App from './component';
import { subscribeForData, pollExists } from './service';
import { subscribeForData, pollExists, wasUserKicked, redirectToLogoutUrl } from './service';
import NavBarContainer from '../nav-bar/container';
import ActionsBarContainer from '../actions-bar/container';
import MediaContainer from '../media/container';
@ -59,11 +58,12 @@ export default createContainer(() => {
Promise.all(subscribeForData())
.then(() => {
setLoading(false);
})
.catch(reason => console.error(reason));
});
return {
wasKicked: wasUserKicked(),
isLoading: getLoading(),
redirectToLogoutUrl,
actionsbar: <ActionsBarContainer />,
};
}, AppContainer);

View File

@ -45,26 +45,51 @@ function subscribeFor(collectionName) {
};
function subscribeToCollections(cb) {
subscribeFor('users').then(() => {
Promise.all(subscribeForData()).then(() => {
if (cb) {
cb();
subscribeFor('users')
.then(() => {
observeUserKick();
return Promise.all(subscribeForData())
.then(() => {
if (cb) {
return cb();
}
});
})
.catch(redirectToLogoutUrl);
};
function redirectToLogoutUrl(reason) {
console.error(reason);
console.log('Redirecting user to the logoutURL...');
document.location.href = Auth.logoutURL;
}
let wasKicked = false;
const wasKickedDep = new Tracker.Dependency;
function observeUserKick() {
Users.find().observe({
removed(old) {
if (old.userId === Auth.userID) {
Auth.clearCredentials(() => {
wasKicked = true;
wasKickedDep.changed();
});
}
});
},
});
};
}
function onStop(error, result) {
console.log('OnError', error, result);
};
function onReady() {
console.log('OnReady');
};
function wasUserKicked() {
wasKickedDep.depend();
return wasKicked;
}
export {
subscribeForData,
setCredentials,
subscribeFor,
subscribeToCollections,
wasUserKicked,
redirectToLogoutUrl
};

View File

@ -0,0 +1,18 @@
import React, { Component } from 'react';
import styles from './styles.scss';
import Icon from '../icon/component';
class KickedScreen extends Component {
render() {
return (
<div className={styles.background}>
<Icon iconName="sad" className={styles.icon}/>
<div className={styles.message}>
{this.props.children}
</div>
</div>
);
}
}
export default KickedScreen;

View File

@ -0,0 +1,28 @@
@import '../../stylesheets/variables/palette';
$bg: $color-gray-dark;
$color: $color-white;
.background {
position: fixed;
display: flex;
flex-flow: column;
justify-content: center;
width: 100%;
height: 100%;
background-color: $bg;
color: $color;
text-align: center;
}
.icon {
width: 100%;
font-size: 10rem;
margin-bottom: 2rem;
}
.message {
text-transform: uppercase;
font-size: 1.25rem;
font-weight: 600;
}

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import styles from './styles.scss';
class Loader extends Component {
class LoadingScreen extends Component {
render() {
return (
<div className={styles.background}>
@ -14,4 +14,4 @@ class Loader extends Component {
);
}
}
export default Loader;
export default LoadingScreen;

View File

@ -51,7 +51,3 @@
.recordImage {
margin-left: -5px;
}
.btn {
border: 10px solid red;
}

View File

@ -393,8 +393,13 @@ class ApiController {
// Check if this config is one of our pre-built config
configxml = configService.getConfig(params.configToken)
if (configxml == null) {
// Default to the default config.
configxml = conf.config;
// BEGIN - backward compatibility
invalid("noConfigFound","We could not find a config for this request.", REDIRECT_RESPONSE);
return
// END - backward compatibility
errors.noConfigFound();
respondWithErrors(errors);
}
} else {
configxml = conf.config;
@ -402,6 +407,11 @@ class ApiController {
} else {
Config conf = meeting.getDefaultConfig();
if (conf == null) {
// BEGIN - backward compatibility
invalid("noConfigFound","We could not find a config for this request.", REDIRECT_RESPONSE);
return
// END - backward compatibility
errors.noConfigFound();
respondWithErrors(errors);
} else {
@ -410,6 +420,11 @@ class ApiController {
}
if (StringUtils.isEmpty(configxml)) {
// BEGIN - backward compatibility
invalid("noConfigFound","We could not find a config for this request.", REDIRECT_RESPONSE);
return
// END - backward compatibility
errors.noConfigFound();
respondWithErrors(errors);
}
@ -1398,11 +1413,19 @@ class ApiController {
String API_CALL = "getDefaultConfigXML"
ApiErrors errors = new ApiErrors();
// BEGIN - backward compatibility
if (StringUtils.isEmpty(params.checksum)) {
invalid("checksumError", "You did not pass the checksum security check")
return
}
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
invalid("checksumError", "You did not pass the checksum security check")
return
}
// END - backward compatibility
// Do we agree on the checksum? If not, complain.
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
errors.checksumError()
@ -1484,7 +1507,7 @@ class ApiController {
reject = true;
else {
us = meetingService.getUserSession(sessionToken);
meeting = meetingService.getMeeting(us.meetingID, true);
meeting = meetingService.getMeeting(us.meetingID);
if (meeting == null || meeting.isForciblyEnded()) {
reject = true
}
@ -1984,17 +2007,11 @@ class ApiController {
// END - backward compatibility
}
// Do we have a publish status? If none, set default value.
String force = params.force
if (StringUtils.isEmpty(force)) {
force = "false"
}
//Execute code specific for this call
Map<String, String> metaParams = ParamsProcessorUtil.processMetaParam(params)
if ( !metaParams.empty ) {
//Proceed with the update
meetingService.updateRecordings(recordIdList, metaParams, force.toBoolean());
meetingService.updateRecordings(recordIdList, metaParams);
}
withFormat {
xml {
@ -2240,7 +2257,7 @@ class ApiController {
//TODO: method added for backward compatibility, it will be removed in next versions after 0.8
private void invalid(key, msg, redirectResponse=false) {
// Note: This xml scheme will be DEPRECATED.
log.debug CONTROLLER_NAME + "#invalid"
log.debug CONTROLLER_NAME + "#invalid " + msg
if (redirectResponse) {
ArrayList<Object> errors = new ArrayList<Object>();
Map<String,String> errorMap = new LinkedHashMap<String,String>()

View File

@ -352,16 +352,15 @@ public class MeetingService implements MessageListener {
}
public Meeting getMeeting(String meetingId) {
return getMeeting(meetingId, false);
}
public Meeting getMeeting(String meetingId, Boolean exactMatch) {
if (meetingId == null)
return null;
int dashes = meetingId.split("-", -1).length - 1;
for (String key : meetings.keySet()) {
if ((!exactMatch && key.startsWith(meetingId))
|| (exactMatch && key.equals(meetingId)))
int keyDashes = key.split("-", -1).length - 1;
if (dashes == 2 && key.equals(meetingId)
|| (dashes < 2 && keyDashes < 2 && key.startsWith(meetingId))) {
return (Meeting) meetings.get(key);
}
}
return null;
@ -485,8 +484,8 @@ public class MeetingService implements MessageListener {
}
}
public void updateRecordings(List<String> idList, Map<String, String> metaParams, boolean force) {
recordingService.updateMetaParams(idList, metaParams, force);
public void updateRecordings(List<String> idList, Map<String, String> metaParams) {
recordingService.updateMetaParams(idList, metaParams);
}
public void processRecording(String meetingId) {

View File

@ -397,10 +397,6 @@ public class RecordingService {
}
public void updateMetaParams(List<String> recordIDs, Map<String,String> metaParams) {
updateMetaParams(recordIDs, metaParams, false);
}
public void updateMetaParams(List<String> recordIDs, Map<String,String> metaParams, boolean force) {
// Define the directories used to lookup the recording
List<String> states = new ArrayList<String>();
@ -422,20 +418,13 @@ public class RecordingService {
Recording rec = getRecordingInfo(recFile);
if (rec != null) {
for (Map.Entry<String,String> meta : metaParams.entrySet()) {
if ( rec.containsMetadata(meta.getKey()) ) {
// The meta parameter already exists
if ( !"".equals(meta.getValue()) || !force ) {
// update it
rec.updateMetadata(meta.getKey(), meta.getValue());
} else {
// delete it
rec.deleteMetadata(meta.getKey());
}
if ( !"".equals(meta.getValue()) ) {
// As it has a value, if the meta parameter exists update it, otherwise add it
rec.updateMetadata(meta.getKey(), meta.getValue());
} else {
// The meta parameter doesn't exist
if ( force ) {
// but force is set to true, then add it
rec.updateMetadata(meta.getKey(), meta.getValue());
// As it doesn't have a value, if it exists delete it
if ( rec.containsMetadata(meta.getKey()) ) {
rec.deleteMetadata(meta.getKey());
}
}
}

View File

@ -28,7 +28,6 @@ require 'recordandplayback/events_archiver'
require 'recordandplayback/video_archiver'
require 'recordandplayback/presentation_archiver'
require 'recordandplayback/deskshare_archiver'
require 'recordandplayback/webrtc_deskshare_archiver'
require 'recordandplayback/generators/events'
require 'recordandplayback/generators/audio'
require 'recordandplayback/generators/video'
@ -126,7 +125,7 @@ module BigBlueButton
def self.exec_ret(*command)
BigBlueButton.logger.info "Executing: #{command.join(' ')}"
IO.popen([*command, :err => [:child, :out]]) do |io|
io.lines.each do |line|
io.each_line do |line|
BigBlueButton.logger.info line.chomp
end
end
@ -140,7 +139,7 @@ module BigBlueButton
IO.pipe do |r, w|
pid = spawn(*command, :out => outio, :err => w)
w.close
r.lines.each do |line|
r.each_line do |line|
BigBlueButton.logger.info line.chomp
end
Process.waitpid(pid)

View File

@ -0,0 +1,399 @@
#!/usr/bin/env python3
# This file is part of BigBlueButton.
#
# BigBlueButton is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BigBlueButton is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
# To install dependencies on Ubuntu:
# apt-get install python3 python3-lxml python3-pyicu
from lxml import etree
from collections import deque
from fractions import Fraction
import io
from icu import Locale, BreakIterator
import unicodedata
import html
import logging
import json
import sys
import os
import argparse
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def webvtt_timestamp(ms):
frac_s = int(ms % 1000)
s = int(ms / 1000 % 60)
m = int(ms / 1000 / 60 % 60)
h = int(ms / 1000 / 60 / 60)
return '{:02}:{:02}:{:02}.{:03}'.format(h, m, s, frac_s)
class CaptionLine:
def __init__(self):
self.text = ""
self.start_time = 0
self.end_time = 0
class Caption:
def __init__(self, locale):
self.locale = locale
self.text = list()
self.timestamps = list()
self._del_timestamps = list()
def apply_edit(self, i, j, timestamp, text):
del_timestamp = None
if j > i:
if self._del_timestamps[i] is not None:
del_timestamp = self._del_timestamps[i]
else:
del_timestamp = self.timestamps[i]
self._del_timestamps[i] = del_timestamp
logger.debug("Removing text %s at %d:%d, del_ts: %d",
repr(''.join(self.text[i:j])), i, j, del_timestamp)
if len(text) > 0:
logger.debug("Inserting text %s at %d:%d, ts: %d",
repr(''.join(text)), i, j, timestamp)
if i < len(self.timestamps) and timestamp > self.timestamps[i]:
timestamp = self._del_timestamps[i]
if timestamp is None and i > 0:
timestamp = self.timestamps[i-1]
logger.debug("Out of order timestamps, using ts: %d", timestamp)
self._del_timestamps[i:j] = [del_timestamp] * len(text)
if (i < len(self._del_timestamps)):
self._del_timestamps[i] = del_timestamp
self.text[i:j] = text
self.timestamps[i:j] = [timestamp] * len(text)
def apply_record_events(self, events):
record = False
ts_offset = 0
stop_ts = 0
start_ts = None
stop_pos = 0
start_pos = None
for event in events:
if event['name'] == 'record_status':
status = event['status']
timestamp = event['timestamp']
if status and not record:
record = True
start_ts = timestamp
logger.debug("Recording started at ts: %d", start_ts)
# Find the position of the first character after recording
# started
start_pos = stop_pos
while start_pos < len(self.timestamps) and \
self.timestamps[start_pos] < start_ts:
start_pos += 1
logger.debug("Replacing characters %d:%d",
stop_pos, start_pos)
self.text[stop_pos:start_pos] = ["\n"]
self.timestamps[stop_pos:start_pos] = [stop_ts - ts_offset]
start_pos = stop_pos + 1
ts_offset += start_ts - stop_ts
logger.debug("Timestamp offset now %d", ts_offset)
stop_ts = None
stop_pos = None
if not status and record:
record = False
stop_ts = timestamp
logger.debug("Recording stopped at ts: %d", stop_ts)
# Find the position of the first character after recording
# stopped, and apply ts offsets
stop_pos = start_pos
while stop_pos < len(self.timestamps) and \
self.timestamps[stop_pos] < stop_ts:
self.timestamps[stop_pos] -= ts_offset
stop_pos += 1
if record:
logger.debug("No recording stop, applying final ts offsets")
while start_pos < len(self.timestamps):
self.timestamps[start_pos] -= ts_offset
start_pos += 1
@classmethod
def from_events(cls, events, apply_record_events=True):
captions = {}
# Apply all of the caption events to generate the full text
# with per-character timestamps
for event in events:
if event['name'] == 'edit_caption_history':
locale = event['locale']
i = event['start_index']
j = event['end_index']
timestamp = event['timestamp']
text = event['text']
caption = captions.get(locale)
if caption is None:
logger.info("Started caption stream for locale '%s'",
locale)
captions[locale] = caption = cls(locale)
caption.apply_edit(i, j, timestamp, text)
if apply_record_events:
for locale, caption in captions.items():
logger.info("Applying recording events to locale '%s'", locale)
caption.apply_record_events(events)
logger.info("Generated %d caption stream(s)", len(captions))
return captions
def split_lines(self, max_length=32):
lines = list()
str_text = "".join(self.text)
locale = Locale(self.locale)
logger.debug("Using locale %s for word-wrapping",
locale.getDisplayName(locale))
break_iter = BreakIterator.createLineInstance(locale)
break_iter.setText(str_text)
line = CaptionLine()
line_start = 0
prev_break = 0
next_break = break_iter.following(prev_break)
# Super simple "greedy" line break algorithm.
while prev_break < len(self.text):
status = break_iter.getRuleStatus()
line_end = next_break
while line_end > line_start and ( \
self.text[line_end-1].isspace() or \
unicodedata.category(self.text[line_end-1]) in ['Cc', 'Mn']
):
line_end -= 1
do_break = False
text_section = unicodedata.normalize(
'NFC', "".join(self.text[line_start:line_end]))
timestamps_section = self.timestamps[line_start:next_break]
start_time = min(timestamps_section)
end_time = max(timestamps_section)
if len(text_section) > max_length:
if prev_break == line_start:
# Over-long string. Just chop it into bits
line_end = next_break = prev_break + max_length
else:
next_break = prev_break
do_break = True
else:
# Status [100,200) indicates a required (hard) line break
if next_break >= len(self.text) or \
(status >= 100 and status < 200):
line.text = text_section
line.start_time = start_time
line.end_time = end_time
do_break = True
if do_break:
logger.debug("text section %d -> %d (%d): %s",
line.start_time, line.end_time,
len(line.text), repr(line.text))
lines.append(line)
line = CaptionLine()
line_start = next_break
else:
line.text = text_section
line.start_time = start_time
line.end_time = end_time
prev_break = next_break
next_break = break_iter.following(prev_break)
return lines
def write_webvtt(self, f):
# Write magic
f.write("WEBVTT\n\n".encode('utf-8'))
lines = self.split_lines()
for i, line in enumerate(lines):
# Don't generate a cue for empty lines
if len(line.text) == 0:
continue
start_time = line.start_time
end_time = line.end_time
if i + 1 < len(lines):
next_start_time = lines[i + 1].start_time
# If the next line is close after the current line, make the
# timestamps continuous so the subtitle doesn't "blink"
if next_start_time - end_time < 1000:
end_time = next_start_time
# Apply some duration cleanup heuristics to give some reasonable
# line durations
duration = end_time - start_time
# Make lines go away if they've been showing for >16 seconds
if duration > 16000:
duration = 16000
# A minimum per-character time for display (up to 3.2s for 32char)
if duration < 100 * len(line.text):
duration = 100 * len(line.text)
# Never show a caption (even a short one) for less than 1s
if duration < 1000:
duration = 1000
end_time = start_time + duration
f.write("{} --> {}\n".format(
webvtt_timestamp(start_time),
webvtt_timestamp(end_time)
).encode('utf-8'))
f.write(html.escape(line.text, quote=False).encode('utf-8'))
f.write("\n\n".encode('utf-8'))
def caption_desc(self):
locale = Locale(self.locale)
return {
"locale": self.locale,
"localeName": locale.getDisplayName(locale)
}
def parse_record_status(event, element):
userId = element.find('userId')
status = element.find('status')
event['name'] = 'record_status'
event['user_id'] = userId.text
event['status'] = (status.text == 'true')
def parse_caption_edit(event, element):
locale = element.find('locale')
text = element.find('text')
startIndex = element.find('startIndex')
endIndex = element.find('endIndex')
localeCode = element.find('localeCode')
event['name'] = 'edit_caption_history'
event['locale_name'] = locale.text
if localeCode is not None:
event['locale'] = localeCode.text
else:
# Fallback for missing 'localeCode'
event['locale'] = "en"
if text.text is None:
event['text'] = list()
else:
event['text'] = list(text.text)
event['start_index'] = int(startIndex.text)
event['end_index'] = int(endIndex.text)
def parse_events(directory="."):
start_time = None
have_record_events = False
events = deque()
with open("{}/events.xml".format(directory), "rb") as f:
for _, element in etree.iterparse(f, tag="event"):
try:
event = {}
# Convert timestamps to be in seconds from recording start
timestamp = int(element.attrib['timestamp'])
if not start_time:
start_time = timestamp
timestamp = timestamp - start_time
# Only need events from these modules
if not element.attrib['module'] in ['CAPTION','PARTICIPANT']:
continue
event['name'] = name = element.attrib['eventname']
event['timestamp'] = timestamp
if name == 'RecordStatusEvent':
parse_record_status(event, element)
have_record_events = True
elif name == 'EditCaptionHistoryEvent':
parse_caption_edit(event, element)
else:
logger.debug("Unhandled event: %s", name)
continue
events.append(event)
finally:
element.clear()
if not have_record_events:
# Add a fake record start event to the events list
event = {
'name': 'record_status',
'user_id': None,
'timestamp': 0,
'status': True
}
events.appendleft(event)
return events
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Generate WebVTT files from BigBlueButton captions",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("-i", "--input", metavar="PATH",
help="input directory with events.xml file",
default=os.curdir)
parser.add_argument("-o", "--output", metavar="PATH",
help="output directory",
default=os.curdir)
args = parser.parse_args()
rawdir = args.input
outputdir = args.output
logger.info("Reading recording events file")
events = parse_events(rawdir)
logger.info("Generating caption data from recording events")
captions = Caption.from_events(events)
for locale, caption in captions.items():
filename = os.path.join(outputdir, "caption_{}.vtt".format(locale))
logger.info("Writing captions for locale %s to %s", locale, filename)
with open(filename, "wb") as f:
caption.write_webvtt(f)
filename = os.path.join(outputdir, "captions.json")
logger.info("Writing captions index file to %s", filename)
caption_descs = [ caption.caption_desc() for caption in captions.values() ]
with open(filename, "w") as f:
json.dump(caption_descs, f)

View File

@ -271,11 +271,6 @@
acorn.$seek.slider('value', currenttime);
}
}
// If captions are active, update them
if(captionsActive) {
updateCaption();
}
};
/*
@ -692,33 +687,14 @@
var captionRadioName = 'acornCaptions' + uniqueID();
var captionOff = function() {
captions = '';
acorn.$caption.hide();
activeCaptions = false;
acorn.$transcriptBtn.removeClass(transcriptBtnActiveClass).hide();
acorn.$transcript.hide();
for (var i = 0; i < acorn.$track.length; i++) {
var track = acorn.$track[i];
track.track.mode = "disabled";
}
acorn.$captionBtn.removeClass(captionBtnActiveClass);
};
/*
* Update caption based on "currentTime"
* Borrowed and adapted from Bruce Lawson's Accessible HTML5 Video with JavaScripted captions
* http://dev.opera.com/articles/view/accessible-html5-video-with-javascripted-captions/
*/
var updateCaption = function() {
var now = acorn.$self[0].currentTime; // how soon is now?
var text = "";
for (var i = 0; i < captions.length; i++) {
if (now >= captions[i].start && now <= captions[i].end) {
text = captions[i].content; // yes? then load it into a variable called text
break;
}
}
acorn.$caption.html(text); // and put contents of text into caption div
};
/*
* Initialize the Caption Selector
* Used when multiple <track>s are present
@ -792,54 +768,33 @@
* Takes the url as a parameter
*/
var loadCaption = function(url) {
// add a loading class to the Caption Button when starting to load the caption
acorn.$captionBtn.addClass(captionBtnLoadingClass);
// make an AJAX request to load the file
$.ajax({
url: url,
success: function(data) {
/*
* On success use a SRT parser on the loaded data
* Using JavaScript SRT parser by Silvia Pfeiffer <silvia@siliva-pfeiffer.de>
* parseSrt included at the end of this file
*/
captions = parseSrt(data);
// Iterate through the available captions, and disable all but the selected one
for (var i = 0; i < acorn.$track.length; i++) {
var track = acorn.$track[i];
if (track.getAttribute('src') == url) {
track.track.mode = "showing";
// TODO transcript markup?
// show the Transcript Button
acorn.$transcriptBtn.show();
//acorn.$transcriptBtn.show();
/*
* Generate the markup for the transcript
* Markup based on Bruce Lawson's Accessible HTML5 Video with JavaScripted captions
* http://dev.opera.com/articles/view/accessible-html5-video-with-javascripted-captions/
*/
var transcriptText = '';
$(captions).each(function() {
transcriptText += '<span data-begin="' + parseInt(this.start, 10) + '" data-end=' + parseInt(this.end, 10) + '>' + this.content.replace("'","") + '</span>';
});
//var transcriptText = '';
//$(captions).each(function() {
// transcriptText += '<span data-begin="' + parseInt(this.start, 10) + '" data-end=' + parseInt(this.end, 10) + '>' + this.content.replace("'","") + '</span>';
//});
// append the generated markup
acorn.$transcript.html(transcriptText);
// show caption
acorn.$caption.show();
captionsActive = true;
// in case the media is paused and timeUpdate is not triggered, trigger it
if(acorn.$self.prop('paused')) {
updateCaption();
}
acorn.$captionBtn.addClass(captionBtnActiveClass).removeClass(captionBtnLoadingClass);
},
error: function() {
// if an error occurs while loading the caption, turn captions off
captionOff();
// if a console is available, log error
if(console) {
console.log('Error loading captions');
}
//acorn.$transcript.html(transcriptText);
} else {
track.track.mode = "disabled";
}
});
}
captionsActive = true;
acorn.$captionBtn.addClass(captionBtnActiveClass);
};
/*
@ -858,6 +813,11 @@
* Caption loading and initialization
*/
var initCaption = function() {
// Check if we have browser support for captions
if (typeof(TextTrack) === "undefined") {
return;
}
// get all <track> elements
acorn.$track = $('track', acorn.$self);
@ -919,7 +879,6 @@
} else {
loadCaption(tracksrc);
}
$(this).toggleClass(captionBtnActiveClass);
});
// load default caption if captionsOn is true
@ -1002,66 +961,3 @@
};
})(jQuery);
/*
* parseSrt function
* JavaScript SRT parser by Silvia Pfeiffer <silvia@siliva-pfeiffer.de>
* http://silvia-pfeiffer.de/
*
* Tri-licensed under MPL 1.1/GPL 2.0/LGPL 2.1
* http://www.gnu.org/licenses/gpl.html
* http://www.gnu.org/licenses/lgpl.html
* http://www.mozilla.org/MPL/
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Silvia Pfeiffer <silvia@siliva-pfeiffer.de>
*
*
*/
function parseSrt(data) {
var srt = data.replace(/\r+/g, ''); // remove dos newlines
srt = srt.replace(/^\s+|\s+$/g, ''); // trim white space start and end
srt = srt.replace(/<[a-zA-Z\/][^>]*>/g, ''); // remove all html tags for security reasons
// get captions
var captions = [];
var caplist = srt.split('\n\n');
for (var i = 0; i < caplist.length; i=i+1) {
var caption = "";
var content, start, end, s;
caption = caplist[i];
s = caption.split(/\n/);
if (s[0].match(/^\d+$/) && s[1].match(/\d+:\d+:\d+/)) {
// ignore caption number in s[0]
// parse time string
var m = s[1].match(/(\d+):(\d+):(\d+)(?:,(\d+))?\s*--?>\s*(\d+):(\d+):(\d+)(?:,(\d+))?/);
if (m) {
start =
(parseInt(m[1], 10) * 60 * 60) +
(parseInt(m[2], 10) * 60) +
(parseInt(m[3], 10)) +
(parseInt(m[4], 10) / 1000);
end =
(parseInt(m[5], 10) * 60 * 60) +
(parseInt(m[6], 10) * 60) +
(parseInt(m[7], 10)) +
(parseInt(m[8], 10) / 1000);
} else {
// Unrecognized timestring
continue;
}
// concatenate text lines to html text
content = s.slice(2).join("<br>");
} else {
// file format error or comment lines
continue;
}
captions.push({start: start, end: end, content: content});
}
return captions;
}

View File

@ -310,6 +310,25 @@ load_video = function(){
webmsource.setAttribute('type','video/webm; codecs="vp8.0, vorbis"');
video.appendChild(webmsource);
// Try to load the captions
// TODO this all should be done asynchronously...
var capReq = new XMLHttpRequest();
capReq.open('GET', RECORDINGS + '/captions.json', /*async=*/false);
capReq.send();
if (capReq.status == 200) {
console.log("==Loading closed captions");
// With sync request, responseType should always be blank (=="text")
var captions = JSON.parse(capReq.responseText);
for (var i = 0; i < captions.length; i++) {
var track = document.createElement("track");
track.setAttribute('kind', 'captions');
track.setAttribute('label', captions[i]['localeName']);
track.setAttribute('srclang', captions[i]['locale']);
track.setAttribute('src', RECORDINGS + '/caption_' + captions[i]['locale'] + '.vtt');
video.appendChild(track);
}
}
/*var time_manager = Popcorn("#video");
var pc_webcam = Popcorn("#webcam");
time_manager.on( "timeupdate", function() {

View File

@ -182,6 +182,12 @@ if not FileTest.directory?(target_dir)
BigBlueButton.process_multiple_videos(target_dir, temp_dir, meeting_id, width, height, presentation_props['audio_offset'], presentation_props['include_deskshare'])
end
BigBlueButton.logger.info("Generating closed captions")
ret = BigBlueButton.exec_ret('utils/gen_webvtt', '-i', raw_archive_dir, '-o', target_dir)
if ret != 0
raise "Generating closed caption files failed"
end
process_done = File.new("#{recording_dir}/status/processed/#{meeting_id}-presentation.done", "w")
process_done.write("Processed #{meeting_id}")
process_done.close

View File

@ -960,6 +960,15 @@ begin
BigBlueButton.logger.info("Copied audio.ogg file")
end
if File.exist?("#{$process_dir}/captions.json")
BigBlueButton.logger.info("Copying caption files")
FileUtils.cp("#{$process_dir}/captions.json", package_dir)
Dir.glob("#{$process_dir}/caption_*.vtt").each do |caption|
BigBlueButton.logger.debug(caption)
FileUtils.cp(caption, package_dir)
end
end
processing_time = File.read("#{$process_dir}/processing_time")