Merge branch 'master' into v1.6

This commit is contained in:
Ken Rice 2016-03-31 20:36:13 -05:00
commit d38d065f51
144 changed files with 13762 additions and 10006 deletions

View File

@ -533,7 +533,7 @@ libs/libzrtp/libzrtp.a:
cd libs/libzrtp && $(MAKE) cd libs/libzrtp && $(MAKE)
libs/libvpx/Makefile: libs/libvpx/Makefile:
cd libs/libvpx && sh ./configure --enable-pic --disable-docs --disable-examples --disable-install-bins --disable-install-srcs --disable-unit-tests --extra-cflags="-fvisibility=hidden" cd libs/libvpx && CC=$(CC) CXX=$(CXX) CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" ./configure --enable-pic --disable-docs --disable-examples --disable-install-bins --disable-install-srcs --disable-unit-tests --extra-cflags="-fvisibility=hidden"
libs/libvpx/libvpx.a: libs/libvpx/Makefile libs/libvpx/libvpx.a: libs/libvpx/Makefile
@cd libs/libvpx && $(MAKE) @cd libs/libvpx && $(MAKE)

View File

@ -1,8 +1,8 @@
<configuration name="httapi.conf" description="HT-TAPI Hypertext Telephony API"> <configuration name="httapi.conf" description="HT-TAPI Hypertext Telephony API">
<settings> <settings>
<!-- print xml on the consol --> <!-- print xml on the console -->
<param name="debug" value="true"/> <param name="debug" value="true"/>
<!-- time to keep audio files when discoverd they were deleted from the http server --> <!-- time to keep audio files when discovered they were deleted from the http server -->
<param name="file-not-found-expires" value="300"/> <param name="file-not-found-expires" value="300"/>
<!-- how often to re-check the server to make sure the remote file has not changed --> <!-- how often to re-check the server to make sure the remote file has not changed -->
<param name="file-cache-ttl" value="300"/> <param name="file-cache-ttl" value="300"/>

View File

@ -31,7 +31,9 @@ case "$1" in
done done
if [ ! -d "/etc/freeswitch" ]; then if [ ! -d "/etc/freeswitch" ]; then
mkdir -p /etc/freeswitch/ mkdir -p /etc/freeswitch/
cp -a /usr/share/freeswitch/conf/vanilla/* /etc/freeswitch/ if [ -e /usr/share/freeswitch/conf/vanilla/freeswitch.xml ]; then
cp -a /usr/share/freeswitch/conf/vanilla/* /etc/freeswitch/
fi
fi fi
if [ ! -d "/etc/freeswitch/tls" ]; then if [ ! -d "/etc/freeswitch/tls" ]; then
mkdir -p /etc/freeswitch/tls/ mkdir -p /etc/freeswitch/tls/

View File

@ -103,7 +103,7 @@
if (moz) { if (moz) {
this.constraints = { this.constraints = {
offerToReceiveAudio: true, offerToReceiveAudio: this.options.useSpeak === "none" ? false : true,
offerToReceiveVideo: this.options.useVideo ? true : false, offerToReceiveVideo: this.options.useVideo ? true : false,
}; };
} else { } else {
@ -111,7 +111,7 @@
optional: [{ optional: [{
'DtlsSrtpKeyAgreement': 'true' 'DtlsSrtpKeyAgreement': 'true'
}],mandatory: { }],mandatory: {
OfferToReceiveAudio: true, OfferToReceiveAudio: this.options.useSpeak === "none" ? false : true,
OfferToReceiveVideo: this.options.useVideo ? true : false, OfferToReceiveVideo: this.options.useVideo ? true : false,
} }
}; };

View File

@ -1308,11 +1308,15 @@
this.modCommand("vid-write-png", null, file); this.modCommand("vid-write-png", null, file);
}; };
$.verto.conf.prototype.setVideoLayout = function(layout) { $.verto.conf.prototype.setVideoLayout = function(layout, canvasID) {
if (!this.params.hasVid) { if (!this.params.hasVid) {
throw 'Conference has no video'; throw 'Conference has no video';
} }
this.modCommand("vid-layout", null, layout); if (canvasID) {
this.modCommand("vid-layout", null, [layout, canvasID]);
} else {
this.modCommand("vid-layout", null, layout);
}
}; };
$.verto.conf.prototype.kick = function(memberID) { $.verto.conf.prototype.kick = function(memberID) {
@ -1439,7 +1443,7 @@
var vlhtml = "<div id='" + vlayout_id + "'><br>" + var vlhtml = "<div id='" + vlayout_id + "'><br>" +
"<b>Video Layout Canvas " + (j+1) + "<b>Video Layout Canvas " + (j+1) +
"</b> <select onChange='$.verto.modfuncs.change_video_layout(\"" + vlayout_id + "\", \"" + j + "\")' id='" + vlselect_id + "'></select> " + "</b> <select onChange='$.verto.modfuncs.change_video_layout(\"" + vlayout_id + "\", \"" + (j+1) + "\")' id='" + vlselect_id + "'></select> " +
"<br><br></div>"; "<br><br></div>";
jq.append(vlhtml); jq.append(vlhtml);
} }
@ -2154,7 +2158,7 @@
var speaker = dialog.useSpeak; var speaker = dialog.useSpeak;
console.info("Using Speaker: ", speaker); console.info("Using Speaker: ", speaker);
if (speaker && speaker !== "any") { if (speaker && speaker !== "any" && speaker !== "none") {
setTimeout(function() { setTimeout(function() {
dialog.setAudioPlaybackDevice(speaker); dialog.setAudioPlaybackDevice(speaker);
}, 500); }, 500);

View File

@ -120,6 +120,7 @@ module.exports = function (grunt) {
'<%= config.app %>/**/*.html', '<%= config.app %>/**/*.html',
'.tmp/styles/{,*/}*.css', '.tmp/styles/{,*/}*.css',
'<%= config.app %>/images/{,*/}*', '<%= config.app %>/images/{,*/}*',
'<%= config.app %>/locales/{,*/}*',
'.tmp/**/*.js', '.tmp/**/*.js',
'<%= config.app %>/**/*.js' '<%= config.app %>/**/*.js'
], ],
@ -150,6 +151,7 @@ module.exports = function (grunt) {
], ],
routes: { routes: {
'/partials': 'src/partials', '/partials': 'src/partials',
'/locales': 'src/locales',
'/config.json': 'src/config.json', '/config.json': 'src/config.json',
'/contributors.txt': 'src/contributors.txt', '/contributors.txt': 'src/contributors.txt',
'/bower_components': './bower_components', '/bower_components': './bower_components',
@ -309,7 +311,8 @@ module.exports = function (grunt) {
'img/*.png', 'img/*.png',
'images/{,*/}*.{webp}', 'images/{,*/}*.{webp}',
'css/fonts/{,*/}*.*', 'css/fonts/{,*/}*.*',
'sounds/*.*' 'sounds/*.*',
'locales/*.*'
] ]
}, { }, {
expand: true, expand: true,

View File

@ -43,7 +43,9 @@
"jquery-json": "~2.5.1", "jquery-json": "~2.5.1",
"datatables": "~1.10.8", "datatables": "~1.10.8",
"angular-bootstrap": "~0.14.3", "angular-bootstrap": "~0.14.3",
"bootstrap-material-design": "~0.3.0" "bootstrap-material-design": "~0.3.0",
"angular-translate": "~2.10.0",
"angular-translate-loader-static-files": "~2.10.0"
}, },
"resolutions": { "resolutions": {
"angular": "~1.3.15", "angular": "~1.3.15",

View File

@ -2,6 +2,7 @@
"Jonatas Oliveira <jonatas@evolux.net.br>", "Jonatas Oliveira <jonatas@evolux.net.br>",
"Ítalo Rossi <italo@evolux.net.br>", "Ítalo Rossi <italo@evolux.net.br>",
"Stefan Yohansson <stefan@evolux.net.br>", "Stefan Yohansson <stefan@evolux.net.br>",
"Victor Torres <victor@evolux.net.br>",
"João Mesquita <jmesquita@indicium.com.ar>", "João Mesquita <jmesquita@indicium.com.ar>",
"Ken Rice <krice@freeswitch.org>", "Ken Rice <krice@freeswitch.org>",
"Brian West <brian@freeswitch.org>" "Brian West <brian@freeswitch.org>"

View File

@ -143,7 +143,7 @@ button.btn i {
-webkit-animation: rotator 1.4s linear infinite; -webkit-animation: rotator 1.4s linear infinite;
animation: rotator 1.4s linear infinite; animation: rotator 1.4s linear infinite;
position: absolute; position: absolute;
top: 35%; top: 50%;
left: 50%; left: 50%;
margin-left: -35px; margin-left: -35px;
zoom: 2; zoom: 2;
@ -503,17 +503,17 @@ body .modal-body .btn-group .btn.active {
}*/ }*/
#incall .video-call { #incall .video-call {
width: 81vw;
height: 82vw; height: 82vw;
max-height: calc(100% - 1vw); max-height: calc(100% - 1vw);
max-width: 146.78vh; max-width: 146.78vh;
margin: auto; margin: 7px auto;
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
margin-top: auto; margin-top: auto;
padding: 1px;
} }
#incall .avatar { #incall .avatar {
@ -764,7 +764,7 @@ body .modal-body .btn-group .btn.active {
} }
#sidebar-wrapper { #sidebar-wrapper {
z-index: 1000; z-index: 8;
position: fixed; position: fixed;
width: 0; width: 0;
height: calc(100% - 60px); height: calc(100% - 60px);
@ -1686,3 +1686,53 @@ body:-webkit-full-screen #incall .video-footer {
from { max-height:2000px;} from { max-height:2000px;}
to { max-height:0px;} to { max-height:0px;}
} }
#settings {
z-index: 9;
position: fixed;
background: rgba(10, 56, 127, 0.9);
width: 100%;
transition: .4s ease-out;
color: white;
max-height: 80%;
overflow: auto;
top: -100%;
padding: 15px 0;
}
#settings select {
color: white;
}
#settings option {
color: #000;
}
#settings .content {
width: 80%;
height: 100%;
margin: auto;
}
#settings.toggled {
top: 67px;
}
#settings .form-group .form-control:focus {
background-image: linear-gradient(#d2d2d2, #d2d2d2), linear-gradient(#d2d2d2, #d2d2d2)
}
#settings .checkbox input[type=checkbox]:checked + .checkbox-material .check,
#settings .checkbox input[type=checkbox]:checked + .checkbox-material .check:before,
#settings .checkbox-default input[type=checkbox]:checked + .checkbox-material .check:before {
color: white;
}
#settings .checkbox .checkbox-material .check {
margin-right: 10px;
}
#settings .btn {
color: rgba(0, 10, 66, 0.84);
background-color: #E8E8E8;
}

View File

@ -49,6 +49,7 @@
<body> <body>
<div ng-include="'partials/menu.html'"></div> <div ng-include="'partials/menu.html'"></div>
<div ng-include="'partials/settings.html'"></div>
<div id="wrapper" class="toggled"> <div id="wrapper" class="toggled">
<!-- Sidebar --> <!-- Sidebar -->
@ -96,6 +97,8 @@
<script src="bower_components/datatables/media/js/jquery.dataTables.js"></script> <script src="bower_components/datatables/media/js/jquery.dataTables.js"></script>
<script src="bower_components/bootstrap-material-design/dist/js/material.js"></script> <script src="bower_components/bootstrap-material-design/dist/js/material.js"></script>
<script src="bower_components/bootstrap-material-design/dist/js/ripples.js"></script> <script src="bower_components/bootstrap-material-design/dist/js/ripples.js"></script>
<script src="bower_components/angular-translate/angular-translate.js"></script>
<script src="bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js"></script>
<!-- endbower --> <!-- endbower -->
<!-- endbuild --> <!-- endbuild -->
@ -125,7 +128,7 @@
<script type="text/javascript" src="src/vertoControllers/controllers/ModalDialpadController.js"></script> <script type="text/javascript" src="src/vertoControllers/controllers/ModalDialpadController.js"></script>
<script type="text/javascript" src="src/vertoControllers/controllers/ModalWsReconnectController.js"></script> <script type="text/javascript" src="src/vertoControllers/controllers/ModalWsReconnectController.js"></script>
<script type="text/javascript" src="src/vertoControllers/controllers/ModalLoginInformationController.js"></script> <script type="text/javascript" src="src/vertoControllers/controllers/ModalLoginInformationController.js"></script>
<script type="text/javascript" src="src/vertoControllers/controllers/ModalSettingsController.js"></script> <script type="text/javascript" src="src/vertoControllers/controllers/SettingsController.js"></script>
<script type="text/javascript" src="src/vertoControllers/controllers/PreviewController.js"></script> <script type="text/javascript" src="src/vertoControllers/controllers/PreviewController.js"></script>
<script type="text/javascript" src="src/vertoDirectives/vertoDirectives.module.js"></script> <script type="text/javascript" src="src/vertoDirectives/vertoDirectives.module.js"></script>

View File

@ -0,0 +1,151 @@
{
"TITLE_ACTIVE_CALL": "Oops, actives Gespräch in Bearbeitung.",
"MESSAGE_ACTIVE_CALL_HANGUP": "Es wärst du bereits in einer Konversation. Konversation beenden?",
"MESSAGE_ACTIVE_CALL_BACK": "Es scheint als warst du in einer Konversation bevor die Sitzung beendet wurde. Diese Konversation fortsetzen?",
"TITLE_INCOMING_CALL": "Eingehender Anruf",
"MESSAGE_INCOMING_CALL": "Von ",
"MESSAGE_NO_HANGUP_CALL": "Es gibt keine Gespräche die beendet werden können.",
"MESSAGE_ENTER_FILENAME": "Bitte, Dateinamen eingeben",
"TITLE_ENABLE_VIDEO": "Video für dieses Gespräch aktivieren?",
"MESSAGE_ENABLE_VIDEO": "Video wird für die nächsten Gespräche aktiviert werden.",
"TITLE_INSERT_BANNER": "Bitte Banner text eingeben",
"TITLE_INSERT_CANVAS_ID": "Bitte Canvas ID eingeben",
"TITLE_INSERT_LAYER": "Please insert the Layer",
"TITLE_TRANSFER": "Gespräch weiterleiten?",
"MESSAGE_TRANSFER": "Welches Ziel soll die Weiterleitung haben?",
"LABEL_TRANSFER": "Ziel",
"MESSAGE_DISPLAY_SETTINGS": "Die Vorschau Einstellungen können während eines Gesprächs nicht angezeigt werden",
"BUTTON_END_CALL": "Anruf beenden",
"BUTTON_CLOSE": "Schließen",
"MESSAGE_PLAY": "Wiedergabe",
"MESSAGE_STOP": "Stoppen",
"MESSAGE_RECORD": "Aufnahme",
"MESSAGE_STOP_RECORD": "Aufnahme beenden",
"MESSAGE_SNAPSHOT": "Snapshot",
"MESSAGE_VIDEO_MODE": "Video Modus",
"MESSAGE_MUTE_MIC": "Mikrofon ein/ausschalten",
"MESSAGE_MUTE_VIDEO": "Video ein/ausschalten",
"MESSAGE_FULLSCREEN": "Vollbildmodus ein/ausschalten",
"MESSAGE_SCREENSHARE": "Bildschirm teilen",
"MESSAGE_OPEN_CLOSE_CHAT": "Chat öffnen/schließen",
"MESSAGE_SPEAKER": "Lautsprecher",
"MESSAGE_POPUP": "Popup",
"CHAT_TITLE_MEMBERS": "Teilnehmer",
"CHAT_TITLE": "Chat",
"CHAT_NO_MEMBERS": "Es gibt keine Mitglieder zum anzeigen.",
"CHAT_GENERAL": "Generell",
"CHAT_TITLE_KICK": "Kick",
"CHAT_KICK": "Kick",
"CHAT_TITLE_VIDEO_FLOOR": "Video Floor",
"CHAT_FLOOR": "Floor",
"CHAT_TITLE_TRANSFER": "Weiterleiten",
"CHAT_TRANSFER": "Weiterleiten",
"CHAT_BANNER": "Banner",
"CHAT_TITLE_SET": "Set",
"CHAT_SET": "Set",
"CHAT_TITLE_RESET": "Resetieren",
"CHAT_RESET": "Resetieren",
"CHAT_CANVAS": "Canvas",
"CHAT_CANVAS_IN": "Canvas In",
"CHAT_CANVAS_OUT": "Canvas Out",
"CHAT_PREV": "Zurück",
"CHAT_NEXT": "Weiter",
"CHAT_LAYER": "Layer",
"CHAT_AUDIO_VIDEO": "Audio/Video",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Mikrofon ein/ausschalten",
"CHAT_MUTE_MIC": "stumm schalten",
"CHAT_UNMUTE_MIC": "stummschaltung deaktivieren",
"CHAT_TITLE_MUTE_UNMUTE_VIDEO": "Stummschaltung ein/ausschalten",
"CHAT_NO_MESSAGES": "Es gibt keine Nachrichten zum anzeigen.",
"CHAT_SEND_MESSAGE": "Absenden",
"CHAT_TYPE_MESSAGE": "Nachricht kann hier eingegeben werden...",
"TITLE_CONTRIBUTORS": "Mitwirkende",
"MESSAGE_CONNECTION_UNTRUSTED": "Die Verbindung ist ungesichert.",
"MESSAGE_TOGGLE_NAVIGATION": "Navigation umschalten",
"BANDWIDTH_INFO": "Info Bandbreite",
"BANDWIDTH_INFO_INCOMING": "Eingehend:",
"BANDWIDTH_INFO_OUTGOING": "Ausgehend:",
"BANDWIDTH_INFO_VIDEO_RES": "Video Auflösung:",
"IN_CALL": "Im Gespräch:",
"LAST_CALL": "Letzter Anruf:",
"OPEN_NEW_WINDOW": "Neues Fenster öffnen",
"CHANGE_LOGIN_INFO": "Anmeldedaten verändern",
"LOGOUT": "Abmelden",
"ABOUT": "Über",
"HELP": "Hilfe",
"CONTRIBUTORS": "Mitwirkende",
"TITLE_PREVIEW_SETTINGS": "Kamera und Mikrofon Einstellungen",
"CAMERA_SETTINGS": "Kamera:",
"MIC_SETTINGS": "Mikrofon:",
"SAVE": "Speichern",
"LOADING": "Ladend",
"ERRORS" : "Fehler",
"CALLING_TO": "Gesprächsaufbau zu ",
"CANCELLING": "Abbrechen...",
"DETERMINING_SPEED": "Geschwindigkeit wird analysiert...",
"CALL_HISTORY": "Gesprächsverlauf",
"CLEAR_CALL_HISTORY": "Gesprächsverlauf löschen",
"NO_CALL_HISTORY": "Kein Gesprächsverlauf vorhanden.",
"ENTER_EXTENSION": "Nummer eingeben",
"CALL_EXTENSION": "Nummer anrufen",
"LOGIN": "Anmelden",
"LOGIN_INFORMATION": "Anmeldeinformationen",
"SAVE_LOGIN_INFORMATION": "Anmeldeinformationen speichern",
"INVALID_LOGIN_FIELDS": "Bitte die unteren Felder kontrollieren und erneut versuchen.",
"NAME": "Name",
"YOUR_NAME": "Dein Name",
"EMAIL": "E-Mail",
"YOUR_EMAIL": "Deine E-Mail",
"USER": "Benutzer",
"PASSWORD": "Passwort",
"CALLER_ID": "Anrufer ID",
"HOSTNAME": "Hostname",
"WEBSOCKET_URL": "Websocket URL",
"SETTINGS": "Einstellungen",
"DEVICE_SETTINGS": "Geräte Einstellungen",
"SHARE_DEVICE": "Gerät teilen",
"SPEAKER": "Lautsprecher:",
"SPEAKER_FEATURE": "Dein browser scheint diese Funktion nicht zu unterstützen",
"PREVIEW_SETTINGS": "Vorschau Einstellungen",
"REFRESH_DEVICE_LIST": "Aktualisieren Geräteliste",
"GENERAL_SETTINGS": "Generelle Einstellungen:",
"USE_VIDEO": "Video aktivieren",
"USE_STEREO_AUDIO": "Stereo Audio",
"USE_STUN": "STUN benützen",
"SCALE_VIDEO": "Entfernte Kamera skalieren damit die Auflösung zusammenpasst",
"ASK_BEFORE_RECOVER": "Nachfrage bevor das Gespräch wiederhergestellt wird",
"BEST_FRAME_RATE": "Beste frame rate:",
"AUDIO_SETTINGS": "Audio Einstellungen:",
"ECHO_CANCEL": "Echo Cancellation",
"NOISE_SUPPRESSION": "Noise Suppression",
"HIGHPASS_FILTER": "Highpass Filter",
"VIDEO_SETTINGS": "Video Einstellungen:",
"REMOTE_ENCODER": "Dedicated Remote Encoder enabled.",
"AUTO_SPEED_RES": "Automatisch geschwindigkeit messen und Auflösung einstellen",
"RECHECK_BANDWIDTH": "Recheck bandwidth before each outgoing call",
"CHECK_NETWORK_SPEED": "Netzwerk Geschwindigkeit messen",
"VIDEO_QUALITY": "Video Qualität:",
"MAX_INCOMING_BANDWIDTH": "Max eingehnde Bandbreite:",
"MAX_OUTGOING_BANDWIDTH": "Max ausgehende Bandbreite:",
"FACTORY_RESET": "Werkseinstellungen",
"SAVE_DEVICE_SETTINGS": "Geräteeinstellungen speichern",
"BROWSER_COMPATIBILITY": "Browserkompatibilität prüfen.",
"REFRESH_MEDIA_DEVICES": "Aktualisiern Medien Geräte.",
"BROWSER_WITHOUT_WEBRTC": "Fehler: Browser unterstützt kein WebRTC.",
"CHECK_PERMISSION_MEDIA": "Medien berechtigungen prüfen",
"CHECK_PROVISIONING_CONF": "Provisioning Konfiguration.",
"CHECK_LOGIN": "Anmeldung verifizieren.",
"CHECK_CONNECTION_SPEED": "Verbindungsgeschwindikeit prüfen.",
"ERROR_PERMISSION_MEDIA": "Fehler: Medien Berechtigung fehlgeschlagen",
"ERROR_PROVISIONING_CONF": "Fehler: Provisioning fehlgeschlagen.",
"PLEASE_WAIT": "Bitte warten...",
"CANCEL": "Abbrechen",
"CHAT_TITLE_VOL_MINUS": "Lautstärke -",
"CHAT_TITLE_VOL_PLUS": "Lautstärke +",
"CHAT_TITLE_GAIN_MINUS": "Gain -",
"CHAT_TITLE_GAIN_PLUS": "Gain +",
"CHAT_VOL_MINUS": "Lautstärke -",
"CHAT_VOL_PLUS": "Lautstärke +",
"CHAT_GAIN_MINUS": "Gain -",
"CHAT_GAIN_PLUS": "Gain +"
}

View File

@ -0,0 +1,151 @@
{
"TITLE_ACTIVE_CALL": "Oops, Active Call in Course.",
"MESSAGE_ACTIVE_CALL_HANGUP": "It seems that you are in a call. Do you want to hang up?",
"MESSAGE_ACTIVE_CALL_BACK": "It seems you were in a call before leaving the last time. Wanna go back to that?",
"TITLE_INCOMING_CALL": "Incoming Call",
"MESSAGE_INCOMING_CALL": "from ",
"MESSAGE_NO_HANGUP_CALL": "There is no call to hangup.",
"MESSAGE_ENTER_FILENAME": "Please, enter filename",
"TITLE_ENABLE_VIDEO": "Would you like to activate video for this call?",
"MESSAGE_ENABLE_VIDEO": "Video will be active during the next calls.",
"TITLE_INSERT_BANNER": "Please insert the banner text",
"TITLE_INSERT_CANVAS_ID": "Please insert the Canvas Id",
"TITLE_INSERT_LAYER": "Please insert the Layer",
"TITLE_TRANSFER": "Transfer party?",
"MESSAGE_TRANSFER": "To what destination would you like to transfer this call?",
"LABEL_TRANSFER": "Destination",
"MESSAGE_DISPLAY_SETTINGS": "Can't display preview settings during a call",
"BUTTON_END_CALL": "End Call",
"BUTTON_CLOSE": "Close",
"MESSAGE_PLAY": "Play",
"MESSAGE_STOP": "Stop",
"MESSAGE_RECORD": "Record",
"MESSAGE_STOP_RECORD": "Stop Record",
"MESSAGE_SNAPSHOT": "Snapshot",
"MESSAGE_VIDEO_MODE": "Video Mode",
"MESSAGE_MUTE_MIC": "(un)Mute Mic",
"MESSAGE_MUTE_VIDEO": "(un)Mute Video",
"MESSAGE_FULLSCREEN": "Toggle Fullscreen Mode",
"MESSAGE_SCREENSHARE": "Screenshare",
"MESSAGE_OPEN_CLOSE_CHAT": "Open/Close Chat",
"MESSAGE_SPEAKER": "Speaker",
"MESSAGE_POPUP": "Popup",
"CHAT_TITLE_MEMBERS": "Members",
"CHAT_TITLE": "Chat",
"CHAT_NO_MEMBERS": "There are no members to show.",
"CHAT_GENERAL": "General",
"CHAT_TITLE_KICK": "Kick",
"CHAT_KICK": "Kick",
"CHAT_TITLE_VIDEO_FLOOR": "Video Floor",
"CHAT_FLOOR": "Floor",
"CHAT_TITLE_TRANSFER": "Transfer",
"CHAT_TRANSFER": "Transfer",
"CHAT_BANNER": "Banner",
"CHAT_TITLE_SET": "Set",
"CHAT_SET": "Set",
"CHAT_TITLE_RESET": "Reset",
"CHAT_RESET": "Reset",
"CHAT_CANVAS": "Canvas",
"CHAT_CANVAS_IN": "Canvas In",
"CHAT_CANVAS_OUT": "Canvas Out",
"CHAT_PREV": "Prev",
"CHAT_NEXT": "Next",
"CHAT_LAYER": "Layer",
"CHAT_AUDIO_VIDEO": "Audio/Video",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Mute/Unmute Mic",
"CHAT_MUTE_MIC": "Mute",
"CHAT_UNMUTE_MIC": "Unmute",
"CHAT_TITLE_MUTE_UNMUTE_VIDEO": "Mute/Unmute Video",
"CHAT_NO_MESSAGES": "There are no messages to show.",
"CHAT_SEND_MESSAGE": "Send",
"CHAT_TYPE_MESSAGE": "Type your message here...",
"TITLE_CONTRIBUTORS": "Contributors",
"MESSAGE_CONNECTION_UNTRUSTED": "This Connection is Untrusted.",
"MESSAGE_TOGGLE_NAVIGATION": "Toggle navigation",
"BANDWIDTH_INFO": "Bandwidth Info",
"BANDWIDTH_INFO_INCOMING": "Incoming:",
"BANDWIDTH_INFO_OUTGOING": "Outgoing:",
"BANDWIDTH_INFO_VIDEO_RES": "Video Resolution:",
"IN_CALL": "In Call:",
"LAST_CALL": "Last Call:",
"OPEN_NEW_WINDOW": "Open New Window",
"CHANGE_LOGIN_INFO": "Change Login Information",
"LOGOUT": "Logout",
"ABOUT": "About",
"HELP": "Help",
"CONTRIBUTORS": "Contributors",
"TITLE_PREVIEW_SETTINGS": "Setup your camera and microphone settings",
"CAMERA_SETTINGS": "Camera:",
"MIC_SETTINGS": "Microphone:",
"SAVE": "Save",
"LOADING": "Loading",
"ERRORS" : "Errors",
"CALLING_TO": "Calling to ",
"CANCELLING": "Cancelling...",
"DETERMINING_SPEED": "Determining your speed...",
"CALL_HISTORY": "Call History",
"CLEAR_CALL_HISTORY": "Clear History",
"NO_CALL_HISTORY": "No history calls.",
"ENTER_EXTENSION": "Enter an extension",
"CALL_EXTENSION": "Call Extension",
"LOGIN": "Login",
"LOGIN_INFORMATION": "Login Information",
"SAVE_LOGIN_INFORMATION": "Save Login Information",
"INVALID_LOGIN_FIELDS": "Verify the fields below and try again.",
"NAME": "Name",
"YOUR_NAME": "Your name",
"EMAIL": "Email",
"YOUR_EMAIL": "Your email",
"USER": "User",
"PASSWORD": "Password",
"CALLER_ID": "Caller ID",
"HOSTNAME": "Hostname",
"WEBSOCKET_URL": "Websocket URL",
"SETTINGS": "Settings",
"DEVICE_SETTINGS": "Device Settings",
"SHARE_DEVICE": "Share device",
"SPEAKER": "Speaker:",
"SPEAKER_FEATURE": "Your browser doesn't seem to support this feature",
"PREVIEW_SETTINGS": "Preview Settings",
"REFRESH_DEVICE_LIST": "Refresh device list",
"GENERAL_SETTINGS": "General settings:",
"USE_VIDEO": "Use Video",
"USE_STEREO_AUDIO": "Stereo Audio",
"USE_STUN": "Use STUN",
"SCALE_VIDEO": "Scale Remote Video To Match Camera Resolution",
"ASK_BEFORE_RECOVER": "Ask before recovering call",
"BEST_FRAME_RATE": "Best frame rate:",
"AUDIO_SETTINGS": "Audio settings:",
"ECHO_CANCEL": "Echo Cancellation",
"NOISE_SUPPRESSION": "Noise Suppression",
"HIGHPASS_FILTER": "Highpass Filter",
"VIDEO_SETTINGS": "Video settings:",
"REMOTE_ENCODER": "Dedicated Remote Encoder enabled.",
"AUTO_SPEED_RES": "Automatically determine speed and resolution settings",
"RECHECK_BANDWIDTH": "Recheck bandwidth before each outgoing call",
"CHECK_NETWORK_SPEED": "Check Network Speed",
"VIDEO_QUALITY": "Video quality:",
"MAX_INCOMING_BANDWIDTH": "Max incoming bandwidth:",
"MAX_OUTGOING_BANDWIDTH": "Max outgoing bandwidth:",
"FACTORY_RESET": "Factory reset",
"SAVE_DEVICE_SETTINGS": "Save Device Settings",
"BROWSER_COMPATIBILITY": "Checking browser compatibility.",
"REFRESH_MEDIA_DEVICES": "Refresh Media Devices.",
"BROWSER_WITHOUT_WEBRTC": "Error: browser doesn't support WebRTC.",
"CHECK_PERMISSION_MEDIA": "Checking media permissions",
"CHECK_PROVISIONING_CONF": "Provisioning configuration.",
"CHECK_LOGIN": "Checking login.",
"CHECK_CONNECTION_SPEED": "Check Connection Speed.",
"ERROR_PERMISSION_MEDIA": "Error: Media Permission Denied",
"ERROR_PROVISIONING_CONF": "Error: Provision failed.",
"PLEASE_WAIT": "Please wait...",
"CANCEL": "Cancel",
"CHAT_TITLE_VOL_MINUS": "Volume -",
"CHAT_TITLE_VOL_PLUS": "Volume +",
"CHAT_TITLE_GAIN_MINUS": "Gain -",
"CHAT_TITLE_GAIN_PLUS": "Gain +",
"CHAT_VOL_MINUS": "Vol -",
"CHAT_VOL_PLUS": "Vol +",
"CHAT_GAIN_MINUS": "Gain -",
"CHAT_GAIN_PLUS": "Gain +"
}

View File

@ -0,0 +1,142 @@
{
"TITLE_ACTIVE_CALL": "Uy, llamada activa en curso.",
"MESSAGE_ACTIVE_CALL_HANGUP": "Parece que estas en una llamada, Desea colgar?",
"MESSAGE_ACTIVE_CALL_BACK": "Parece que estabas en una llamada la ultima vez, quieres regresar a eso?",
"TITLE_INCOMING_CALL": "Llamada entrante",
"MESSAGE_INCOMING_CALL": "Desde ",
"MESSAGE_NO_HANGUP_CALL": "No hay ninguna llamada que colgar.",
"MESSAGE_ENTER_FILENAME": "Por favor, indique el nombre del archivo.",
"TITLE_ENABLE_VIDEO": "¿Quieres activar el video para esta convocatoria?",
"MESSAGE_ENABLE_VIDEO": "Video estará activo durante las siguientes llamadas.",
"TITLE_INSERT_BANNER": "Por favor introduzca el texto del estandarte ",
"TITLE_INSERT_CANVAS_ID": "Por favor, Introduzca el Id de lona",
"TITLE_INSERT_LAYER": "Por favor inserte la capa",
"TITLE_TRANSFER": "¿Transferencia de partido?",
"MESSAGE_TRANSFER": "¿A qué destino quieres transferir esta llamada?",
"LABEL_TRANSFER": "Destino",
"MESSAGE_DISPLAY_SETTINGS": "No se puede mostrar la configuración durante una llamada",
"BUTTON_END_CALL": "Finalizar llamada",
"BUTTON_CLOSE": "Cerrar",
"MESSAGE_PLAY": " Reproducir",
"MESSAGE_STOP": "Parar",
"MESSAGE_RECORD": "Grabar",
"MESSAGE_STOP_RECORD": "Parar de grabar",
"MESSAGE_SNAPSHOT": "Foto instantánea",
"MESSAGE_VIDEO_MODE": "Modo de vídeo",
"MESSAGE_MUTE_MIC": "(des) silenciar el micrófono",
"MESSAGE_MUTE_VIDEO": "(des) silenciar el Video",
"MESSAGE_FULLSCREEN": "Cambiar el modo de pantalla completa",
"MESSAGE_SCREENSHARE": "Compartir la pantalla",
"MESSAGE_OPEN_CLOSE_CHAT": "Abrir/cerrar Chat",
"MESSAGE_SPEAKER": "Altavoz",
"MESSAGE_POPUP": "Emergente",
"CHAT_TITLE_MEMBERS": "Miembros",
"CHAT_TITLE": "Chat",
"CHAT_NO_MEMBERS": "No hay miembros para mostrar.",
"CHAT_GENERAL": "General",
"CHAT_TITLE_KICK": "Patear",
"CHAT_KICK": "Patear",
"CHAT_TITLE_VIDEO_FLOOR": "Piso Video",
"CHAT_FLOOR": "Piso",
"CHAT_TITLE_TRANSFER": "Transferir",
"CHAT_TRANSFER": "Transferir",
"CHAT_BANNER": "Estandarte",
"CHAT_TITLE_SET": "Poner",
"CHAT_SET": "Poner",
"CHAT_TITLE_RESET": "Reajustar",
"CHAT_RESET": "Reajustar ",
"CHAT_CANVAS": "Lona",
"CHAT_CANVAS_IN": "Lona dentro",
"CHAT_CANVAS_OUT": "Lona afuera",
"CHAT_PREV": "Prev",
"CHAT_NEXT": "Proximo",
"CHAT_LAYER": "Capa",
"CHAT_AUDIO_VIDEO": "Audio/Video",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Silenciar/Des Silenciar Microfono",
"CHAT_MUTE_MIC": "Silenciar ",
"CHAT_UNMUTE_MIC": "Des Silenciar",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Silenciar/Des Silenciar Video",
"CHAT_NO_MESSAGES": "No hay mensajes para mostrar.",
"CHAT_SEND_MESSAGE": "Enviar",
"CHAT_TYPE_MESSAGE": "Escriba su mensaje aquí...",
"TITLE_CONTRIBUTORS": "Contribuyente",
"MESSAGE_CONNECTION_UNTRUSTED": "Esta conexión no es de confianza.",
"MESSAGE_TOGGLE_NAVIGATION": "Alternar la navegación",
"BANDWIDTH_INFO": "Información de la amplitud de banda",
"BANDWIDTH_INFO_INCOMING": "Entrante:",
"BANDWIDTH_INFO_OUTGOING": "Saliente:",
"BANDWIDTH_INFO_VIDEO_RES": "Resolución de vídeo:",
"IN_CALL": "En llamada:",
"LAST_CALL": " Última llamada:",
"OPEN_NEW_WINDOW": "Abrir nueva Ventana",
"CHANGE_LOGIN_INFO": "Cambiar la información de inicio de sesión",
"LOGOUT": "Salir de sesión",
"ABOUT": "Sobre",
"HELP": "Ayuda",
"CONTRIBUTORS": "Contribuyente",
"TITLE_PREVIEW_SETTINGS": "Configuración de la Cámara y el Micrófono",
"CAMERA_SETTINGS": "Cámara:",
"MIC_SETTINGS": "Micrófono:",
"SAVE": "Salvar",
"LOADING": "Cargando",
"ERRORS" : "Errores",
"CALLING_TO": "Llamar a ",
"CANCELLING": "Cancelando...",
"DETERMINING_SPEED": "Determinación de la velocidad...",
"CALL_HISTORY": "Historial de llamadas",
"CLEAR_CALL_HISTORY": "Borrar Historial",
"NO_CALL_HISTORY": "No Historial de llamadas.",
"ENTER_EXTENSION": "Entre en una extensión",
"CALL_EXTENSION": " Llamar extensión",
"LOGIN": "Inicio de Sesión",
"LOGIN_INFORMATION": "Información de Inicio de Sesión",
"SAVE_LOGIN_INFORMATION": "Salvar Información de Inicio de Sesión",
"INVALID_LOGIN_FIELDS": "Verifique los campos abajo e intente otra vez.",
"NAME": "Nombre",
"YOUR_NAME": "Su Nombre",
"EMAIL": " El Correo Electrónico",
"YOUR_EMAIL": "Su Correo Electrónico",
"USER": "Usuario",
"PASSWORD": "Contraseña",
"CALLER_ID": "Identificador de llamadas",
"HOSTNAME": "Hostname",
"WEBSOCKET_URL": "Websocket URL",
"SETTINGS": "Configuraciónes",
"DEVICE_SETTINGS": "La configuración de usuario",
"SHARE_DEVICE": "Compartir dispositivo",
"SPEAKER": "Altavoz:",
"SPEAKER_FEATURE": "Su navegador no parece apoyar esta función",
"PREVIEW_SETTINGS": "Configuración de vista anticipada",
"REFRESH_DEVICE_LIST": "Actualizar la lista de dispositivos",
"GENERAL_SETTINGS": "Configuración general:",
"USE_VIDEO": "Utilizar vídeo",
"USE_STEREO_AUDIO": "Audio Estéreo",
"USE_STUN": "Utilizar STUN",
"SCALE_VIDEO": "Escala de vídeo remoto para que coincida con la resolución de la cámara",
"ASK_BEFORE_RECOVER": "Preguntar antes de recuperar la llamada",
"BEST_FRAME_RATE": "Mejor velocidad:",
"AUDIO_SETTINGS": "Configuración de audio:",
"ECHO_CANCEL": "Cancelación de eco",
"NOISE_SUPPRESSION": "Supresión de Ruido",
"HIGHPASS_FILTER": "Filtro de paso alto",
"VIDEO_SETTINGS": "Configuración de vídeo:",
"REMOTE_ENCODER": "Codificador Remoto dedicado permitido.",
"AUTO_SPEED_RES": "Determinar automáticamente la configuración de velocidad y resolución",
"RECHECK_BANDWIDTH": "Compruebe de nuevo la amplitud de banda antes de cada llamada saliente",
"CHECK_NETWORK_SPEED": "Compruebe la velocidad de la red",
"VIDEO_QUALITY": "Calidad de video:",
"MAX_INCOMING_BANDWIDTH": "Máxima amplitude banda entrante:",
"MAX_OUTGOING_BANDWIDTH": "Máxima amplitude banda saliente:",
"FACTORY_RESET": "Restablecimiento de fábrica",
"SAVE_DEVICE_SETTINGS": "Guardar la Configuración del dispositivo ",
"BROWSER_COMPATIBILITY": "Comprobar Compatibilidad de navegadores.",
"REFRESH_MEDIA_DEVICES": "Actualizar dispositivos de medios.",
"BROWSER_WITHOUT_WEBRTC": "Error: el navegador no soporta WebRTC.",
"CHECK_PERMISSION_MEDIA": "Comprobación de permisos de los medios de comunicación",
"CHECK_PROVISIONING_CONF": "Aprovisionamiento de configuración.",
"CHECK_LOGIN": "Verifiicar inicio de sesión.",
"CHECK_CONNECTION_SPEED": " Verificar la velocidad de conexión.",
"ERROR_PERMISSION_MEDIA": "Error: permiso de medios negado",
"ERROR_PROVISIONING_CONF": "Error: Provisión falló.",
"PLEASE_WAIT": "Por favor espere..."
}

View File

@ -0,0 +1,143 @@
{
"TITLE_ACTIVE_CALL": "Oups, appel actif déjà en cours",
"MESSAGE_ACTIVE_CALL_HANGUP": "Il semble que vous êtes déjà en communication. Voulez vous raccrocher ?",
"MESSAGE_ACTIVE_CALL_BACK": "Il semblre que vous étiez déjà en appel avant de quitter la dernière fois, Voulez-vous retourner à cet appel ?",
"TITLE_INCOMING_CALL": "Appel entrant",
"MESSAGE_INCOMING_CALL": "De ",
"MESSAGE_NO_HANGUP_CALL": "Il n'y as pas d'appels à raccrocher.",
"MESSAGE_ENTER_FILENAME": "Veuillez entrer le nom de fichier",
"TITLE_ENABLE_VIDEO": "Voulez vous activer la video pour cet appel?",
"MESSAGE_ENABLE_VIDEO": "La vidéo sera activée pour les prochains appels.",
"TITLE_INSERT_BANNER": "Merci d'insérer le texte de bannière",
"TITLE_INSERT_CANVAS_ID": "Merci d'insérer l'ID du Canvas",
"TITLE_INSERT_LAYER": "Merci d'insérer la couche",
"TITLE_TRANSFER": "Transferer le correspondant ?",
"MESSAGE_TRANSFER": "Vers quelle destination voulez vous transférer cet appel ?",
"LABEL_TRANSFER": "Destination",
"MESSAGE_DISPLAY_SETTINGS": "Nous ne pouvons afficher les paramêtres de prévisualisation pendant un appel",
"BUTTON_END_CALL": "Terminer l'appel",
"BUTTON_CLOSE": "Fermer",
"MESSAGE_PLAY": "Jouer",
"MESSAGE_STOP": "Stop",
"MESSAGE_RECORD": "Enregistrer",
"MESSAGE_STOP_RECORD": "Arrêter l'enregistrement",
"MESSAGE_SNAPSHOT": "Capture d'écran",
"MESSAGE_VIDEO_MODE": "Mode Video",
"MESSAGE_MUTE_MIC": "(des)activer Micro",
"MESSAGE_MUTE_VIDEO": "(des)activer Video",
"MESSAGE_FULLSCREEN": "Basculer en mode plein écran",
"MESSAGE_SCREENSHARE": "Partage d'écran",
"MESSAGE_OPEN_CLOSE_CHAT": "Ouvrir/Fermer le chat",
"MESSAGE_SPEAKER": "Orateur",
"MESSAGE_POPUP": "Popup",
"CHAT_TITLE_MEMBERS": "Membres",
"CHAT_TITLE": "Chat",
"CHAT_NO_MEMBERS": "Il n'y as pas de membres actuellement.",
"CHAT_GENERAL": "General",
"CHAT_TITLE_KICK": "Ejecter",
"CHAT_KICK": "Ejecter",
"CHAT_TITLE_VIDEO_FLOOR": "Video Floor",
"CHAT_FLOOR": "Floor",
"CHAT_TITLE_TRANSFER": "Transferer",
"CHAT_TRANSFER": "Transferer",
"CHAT_BANNER": "Bannière",
"CHAT_TITLE_SET": "Set",
"CHAT_SET": "Set",
"CHAT_TITLE_RESET": "Reset",
"CHAT_RESET": "Reset",
"CHAT_CANVAS": "Canvas",
"CHAT_CANVAS_IN": "Canvas In",
"CHAT_CANVAS_OUT": "Canvas Out",
"CHAT_PREV": "Précédent",
"CHAT_NEXT": "Suivant",
"CHAT_LAYER": "Couche",
"CHAT_AUDIO_VIDEO": "Audio/Video",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Activer/Désactiver micro",
"CHAT_MUTE_MIC": "Désactiver le micro",
"CHAT_UNMUTE_MIC": "Activer le micro",
"CHAT_TITLE_MUTE_UNMUTE_VIDEO": "Activer/Désactiver video",
"CHAT_NO_MESSAGES": "il n'y as pas de messages à afficher",
"CHAT_SEND_MESSAGE": "Envoyer",
"CHAT_TYPE_MESSAGE": "Saisir votre message ici...",
"TITLE_CONTRIBUTORS": "Contributeurs",
"MESSAGE_CONNECTION_UNTRUSTED": "Cette connection n'est pas certifiée sûre.",
"MESSAGE_TOGGLE_NAVIGATION": "Basculer la navigation",
"BANDWIDTH_INFO": "Information de bande passante",
"BANDWIDTH_INFO_INCOMING": "Entrant:",
"BANDWIDTH_INFO_OUTGOING": "Sortant:",
"BANDWIDTH_INFO_VIDEO_RES": "Résolution vidéo:",
"IN_CALL": "Appel en cours:",
"LAST_CALL": "Dernier appel:",
"OPEN_NEW_WINDOW": "Ouvrir une nouvelle fenêtre",
"CHANGE_LOGIN_INFO": "Changer les informations utilisateur",
"LOGOUT": "Deconnection",
"ABOUT": "A propos",
"HELP": "Aide",
"CONTRIBUTORS": "Contributeurs",
"TITLE_PREVIEW_SETTINGS": "Paramêtrer votre micro et caméra",
"CAMERA_SETTINGS": "Camera:",
"MIC_SETTINGS": "Microphone:",
"SAVE": "Sauvegarder",
"LOADING": "Chargement",
"ERRORS" : "Erreurs",
"CALLING_TO": "Appel sortant vers ",
"CANCELLING": "Annulation en cours...",
"DETERMINING_SPEED": "En train de déterminer votre bande passante...",
"CALL_HISTORY": "Historique d'appel",
"CLEAR_CALL_HISTORY": "Effacer l'historique",
"NO_CALL_HISTORY": "Pas d'historique d'appel",
"ENTER_EXTENSION": "Saisir une extension",
"CALL_EXTENSION": "Appeler une extension",
"LOGIN": "Nom d'utilisateur:",
"LOGIN_INFORMATION": "Information utilisateur",
"SAVE_LOGIN_INFORMATION": "Sauvegarder les informations utilisateurs",
"INVALID_LOGIN_FIELDS": "Vérifiez les champs ci dessous et rééssayez.",
"NAME": "Nom",
"YOUR_NAME": "Votre nom",
"EMAIL": "Email",
"YOUR_EMAIL": "Votre email",
"USER": "Utilisateur",
"PASSWORD": "mot de passe",
"CALLER_ID": "Numéro Appelant",
"HOSTNAME": "Nom de domaine",
"WEBSOCKET_URL": "URL Websocket",
"SETTINGS": "Paramêtres",
"DEVICE_SETTINGS": "Paramêtres de l'appareil",
"SHARE_DEVICE": "Partager le périphérique",
"SPEAKER": "Orateur:",
"SPEAKER_FEATURE": "Votre navigateur ne semble pas supporter cette fonctionnalité",
"PREVIEW_SETTINGS": "Paramêtres de prévisualisation",
"REFRESH_DEVICE_LIST": "Rafraichir la liste des périphériques",
"GENERAL_SETTINGS": "Paramêtres généraux:",
"USE_VIDEO": "Utiliser la vidéo",
"USE_STEREO_AUDIO": "Audio stereo",
"USE_STUN": "Utiliser STUN",
"SCALE_VIDEO": "Ajuster la résolution de la vidéo distante pour correspondre à la résolution de la caméra",
"ASK_BEFORE_RECOVER": "Demander avant de récupérer un appel",
"BEST_FRAME_RATE": "Meilleur taux de rafraichissement:",
"AUDIO_SETTINGS": "Paramêtres audio:",
"ECHO_CANCEL": "Anti-echo",
"NOISE_SUPPRESSION": "Suppression du bruit",
"HIGHPASS_FILTER": "Filtre passe-haut:",
"VIDEO_SETTINGS": "Paramêtres vidéo:",
"REMOTE_ENCODER": "Encodeur distant dédié activé",
"AUTO_SPEED_RES": "Détecter automatiquement les paramêtres de bande passante et de résolution vidéo",
"RECHECK_BANDWIDTH": "Revérifier la bande passante avant chaque appel",
"CHECK_NETWORK_SPEED": "Verification de la vitesse de connection.",
"VIDEO_QUALITY": "Qualité vidéo:",
"MAX_INCOMING_BANDWIDTH": "Bande passante entrante maximale:",
"MAX_OUTGOING_BANDWIDTH": "Bande passante sortante maximale:",
"FACTORY_RESET": "Remise aux paramêtres par défaut",
"SAVE_DEVICE_SETTINGS": "Sauvegarder les paramêtres de l'appareil.",
"BROWSER_COMPATIBILITY": "Vérification de la compatibilité du navigateur.",
"REFRESH_MEDIA_DEVICES": "Rafraichir les périphériques multimédias.",
"BROWSER_WITHOUT_WEBRTC": "Erreur: votre navigateur ne supporte pas WebRTC.",
"CHECK_PERMISSION_MEDIA": "Vérification des permissions multimédias",
"CHECK_PROVISIONING_CONF": "Vérification de la configuration.",
"CHECK_LOGIN": "Vérification du nom d'utilisateur",
"CHECK_CONNECTION_SPEED": "Vérifiez votre vitesse de connection à Internet.",
"ERROR_PERMISSION_MEDIA": "Erreur: La permission d'accéder aux périphériques multimedia as été refusée",
"ERROR_PROVISIONING_CONF": "Erreur: La configuration as échouée.",
"PLEASE_WAIT": "Merci de patienter...",
"CANCEL": "Annuler"
}

View File

@ -0,0 +1,143 @@
{
"TITLE_ACTIVE_CALL": "Waduh, Panggilan sedang berlangsung .",
"MESSAGE_ACTIVE_CALL_HANGUP": "Tampaknya kamu sedang dalam percakapan. Mau diputus?",
"MESSAGE_ACTIVE_CALL_BACK": "Sebelum ini, Tampaknya kamu sedang dalam percakapan. Mau disambungkan lagi?",
"TITLE_INCOMING_CALL": "Panggilan masuk",
"MESSAGE_INCOMING_CALL": "dari ",
"MESSAGE_NO_HANGUP_CALL": "Tidak ada panggilan yang perlu diputus.",
"MESSAGE_ENTER_FILENAME": "Silahkan masukan nama file",
"TITLE_ENABLE_VIDEO": "Apakah anda ingin mengaktifkan Video di panggilan ini?",
"MESSAGE_ENABLE_VIDEO": "Video akan diaktifkan di panggilan berikutnya.",
"TITLE_INSERT_BANNER": "Silahkan isi dengan teks banner",
"TITLE_INSERT_CANVAS_ID": "Silahkan isi dengan id canvas",
"TITLE_INSERT_LAYER": "Silahkan isi dengan layer",
"TITLE_TRANSFER": "Panggilan dialihkan?",
"MESSAGE_TRANSFER": "Kemana panggilan anda ingin dialihkan?",
"LABEL_TRANSFER": "Tujuan",
"MESSAGE_DISPLAY_SETTINGS": "Tidak dapat mereview setelan, selama dalam panggilan",
"BUTTON_END_CALL": "Mengakhiri Panggilan",
"BUTTON_CLOSE": "Tutup",
"MESSAGE_PLAY": "Mainkan",
"MESSAGE_STOP": "Hentikan",
"MESSAGE_RECORD": "Catat",
"MESSAGE_STOP_RECORD": "Hentikan Pencatatan",
"MESSAGE_SNAPSHOT": "Jepret",
"MESSAGE_VIDEO_MODE": "Mode Video",
"MESSAGE_MUTE_MIC": "(tidak)Aktifkan Mic",
"MESSAGE_MUTE_VIDEO": "(tidak)Aktifkan Video",
"MESSAGE_FULLSCREEN": "Pilihan Mode Layar Penuh",
"MESSAGE_SCREENSHARE": "Berbagi Layar",
"MESSAGE_OPEN_CLOSE_CHAT": "Buka/Tutup Obrolan",
"MESSAGE_SPEAKER": "Speaker",
"MESSAGE_POPUP": "Muncul",
"CHAT_TITLE_MEMBERS": "Anggota",
"CHAT_TITLE": "Obrolan",
"CHAT_NO_MEMBERS": "Tidak ada anggota untuk ditampilkan.",
"CHAT_GENERAL": "Umum",
"CHAT_TITLE_KICK": "Tendang",
"CHAT_KICK": "Tendang",
"CHAT_TITLE_VIDEO_FLOOR": "Video Latar",
"CHAT_FLOOR": "Latar",
"CHAT_TITLE_TRANSFER": "Alihkan",
"CHAT_TRANSFER": "Alihkan",
"CHAT_BANNER": "Banner",
"CHAT_TITLE_SET": "Setelan",
"CHAT_SET": "Setelan",
"CHAT_TITLE_RESET": "Atur Ulang",
"CHAT_RESET": "Atur Ulang",
"CHAT_CANVAS": "Canvas",
"CHAT_CANVAS_IN": "Canvas Masuk",
"CHAT_CANVAS_OUT": "Canvas Keluar",
"CHAT_PREV": "Sebelumnya",
"CHAT_NEXT": "Berikutnya",
"CHAT_LAYER": "Layer",
"CHAT_AUDIO_VIDEO": "Suara/Video",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Matikan/Aktifkan Mic",
"CHAT_MUTE_MIC": "Matikan",
"CHAT_UNMUTE_MIC": "Aktifkan",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Matikan/Aktifkan Video",
"CHAT_NO_MESSAGES": "Tidak ada pesan yang perlu ditampilkan.",
"CHAT_SEND_MESSAGE": "Kirim",
"CHAT_TYPE_MESSAGE": "Ketik pesan kamu disini...",
"TITLE_CONTRIBUTORS": "Penyumbang",
"MESSAGE_CONNECTION_UNTRUSTED": "Sambungan ini tidak bisa dipercaya.",
"MESSAGE_TOGGLE_NAVIGATION": "Pilihan navigasi",
"BANDWIDTH_INFO": "Info Bandwith",
"BANDWIDTH_INFO_INCOMING": "Panggilan Masuk:",
"BANDWIDTH_INFO_OUTGOING": "Panggilan Keluar:",
"BANDWIDTH_INFO_VIDEO_RES": "Resolusi Video:",
"IN_CALL": "Dalam Panggilan:",
"LAST_CALL": "Panggilan Terakhir:",
"OPEN_NEW_WINDOW": "Buka Tampilan Baru",
"CHANGE_LOGIN_INFO": "Ganti Informasi Login",
"LOGOUT": "Keluar",
"ABOUT": "Tentang",
"HELP": "Bantuan",
"CONTRIBUTORS": "Penyumbang",
"TITLE_PREVIEW_SETTINGS": "Setel Kamera dan Mikrophone kamu",
"CAMERA_SETTINGS": "Kamera:",
"MIC_SETTINGS": "Mikrophone:",
"SAVE": "Simpan",
"LOADING": "Sedang di muat",
"ERRORS" : "Kesalahan",
"CALLING_TO": "Panggilan ke ",
"CANCELLING": "Membatalkan...",
"DETERMINING_SPEED": "Mengukur kecepatan kamu...",
"CALL_HISTORY": "Riwayat Panggilan",
"CLEAR_CALL_HISTORY": "Hapus Riwayat",
"NO_CALL_HISTORY": "Tidak ada Riwayat Panggilan.",
"ENTER_EXTENSION": "Ketikkan Nomer Ekstensi",
"CALL_EXTENSION": "Panggilan Ke Nomer Ekstensi",
"LOGIN": "Login",
"LOGIN_INFORMATION": "Informasi Login",
"SAVE_LOGIN_INFORMATION": "Simpan Informasi Login",
"INVALID_LOGIN_FIELDS": "Periksa isian dibawah ini dan periksa lagi.",
"NAME": "Nama",
"YOUR_NAME": "Nama kamu",
"EMAIL": "Email",
"YOUR_EMAIL": "Email kamu",
"USER": "User",
"PASSWORD": "Password",
"CALLER_ID": "ID Pemanggil",
"HOSTNAME": "Hostname",
"WEBSOCKET_URL": "Websocket URL",
"SETTINGS": "Setelan",
"DEVICE_SETTINGS": "Setelan Perangkat",
"SHARE_DEVICE": "Berbagi Perangkat",
"SPEAKER": "Speaker:",
"SPEAKER_FEATURE": "Tampaknya browser kamu tidak mendukung feature ini",
"PREVIEW_SETTINGS": "Mereview Setelan",
"REFRESH_DEVICE_LIST": "Data ulang daftar perangkat",
"GENERAL_SETTINGS": "Setelan Umum:",
"USE_VIDEO": "Menggunakan Video",
"USE_STEREO_AUDIO": "Suara Stereo",
"USE_STUN": "Menggunakan STUN",
"SCALE_VIDEO": "Menyesuaikan skala video sisi jauh dengan resolusi kamera",
"ASK_BEFORE_RECOVER": "Bertanya sebelum memulihkan panggilan",
"BEST_FRAME_RATE": "Kecepatan frame terbaik:",
"AUDIO_SETTINGS": "Setelan Suara:",
"ECHO_CANCEL": "Membuang Gema",
"NOISE_SUPPRESSION": "Meminimalkan Gangguan",
"HIGHPASS_FILTER": "Highpass Filter",
"VIDEO_SETTINGS": "Setelan Video:",
"REMOTE_ENCODER": "Encoder sisi jauh diaktifkan.",
"AUTO_SPEED_RES": "Menentukan setelan kecepatan dan resolusi secara otomatis",
"RECHECK_BANDWIDTH": "Memastikan bandwidth sebelum setiap panggilan keluar",
"CHECK_NETWORK_SPEED": "Memastikan kecepatan Jaringan",
"VIDEO_QUALITY": "Kualitas Video:",
"MAX_INCOMING_BANDWIDTH": "Maksimum bandwith masuk:",
"MAX_OUTGOING_BANDWIDTH": "Maksimum bandwith keluar:",
"FACTORY_RESET": "Kembali ke Setelan Pabrik",
"SAVE_DEVICE_SETTINGS": "Simpan Setelan Perngkat",
"BROWSER_COMPATIBILITY": "Memastikan kecocokan browser.",
"REFRESH_MEDIA_DEVICES": "Mendata ulang perangkat media.",
"BROWSER_WITHOUT_WEBRTC": "Salah: browser tidak mendukung WebRTC.",
"CHECK_PERMISSION_MEDIA": "Memastikan izin dari perangkat",
"CHECK_PROVISIONING_CONF": "Konfigurasi Provisioning.",
"CHECK_LOGIN": "Memastikan login.",
"CHECK_CONNECTION_SPEED": "Memastikan kecepatan koneksi.",
"ERROR_PERMISSION_MEDIA": "Salah: Izin media ditolak",
"ERROR_PROVISIONING_CONF": "Salah: Provision gagal.",
"PLEASE_WAIT": "Tunggu..."
}

View File

@ -0,0 +1,153 @@
{
"TITLE_ACTIVE_CALL": "Oops, Chiamata in corso.",
"MESSAGE_ACTIVE_CALL_HANGUP": "Sembra che tu sia in conversazione. Vuoi chiudere la chiamata?",
"MESSAGE_ACTIVE_CALL_BACK": "Sembra che eri in conversazione prima di abbandonare la sessione l'ultima volta. Vuoi tornare in quella conversazione?",
"TITLE_INCOMING_CALL": "Chiamata in arrivo",
"MESSAGE_INCOMING_CALL": "da ",
"MESSAGE_NO_HANGUP_CALL": "Non ci sono chiamate da chiudere.",
"MESSAGE_ENTER_FILENAME": "Per favore, inserisci il nome del file",
"TITLE_ENABLE_VIDEO": "Vuoi attivare il video per questa chiamata?",
"MESSAGE_ENABLE_VIDEO": "Il video verrà attivato a partire dalla prossima chiamata.",
"TITLE_INSERT_BANNER": "Per favore inserisci il testo del banner",
"TITLE_INSERT_CANVAS_ID": "Please insert the Canvas Id",
"TITLE_INSERT_LAYER": "Please insert the Layer",
"TITLE_TRANSFER": "Transfer party?",
"MESSAGE_TRANSFER": "To what destination would you like to transfer this call?",
"LABEL_TRANSFER": "Destinazione",
"MESSAGE_DISPLAY_SETTINGS": "Non è possibile mostrare le configurazioni video durante una chiamata",
"BUTTON_END_CALL": "Termina la chiamata",
"BUTTON_CLOSE": "Chiudi",
"MESSAGE_PLAY": "Riproduci",
"MESSAGE_STOP": "Ferma",
"MESSAGE_RECORD": "Registra",
"MESSAGE_STOP_RECORD": "Ferma la registrazione",
"MESSAGE_SNAPSHOT": "Snapshot",
"MESSAGE_VIDEO_MODE": "Video Mode",
"MESSAGE_MUTE_MIC": "(un)Mute Mic",
"MESSAGE_MUTE_VIDEO": "(un)Mute Video",
"MESSAGE_FULLSCREEN": "Abilita/Disabilita schermo intero",
"MESSAGE_SCREENSHARE": "Condividi lo schermo",
"MESSAGE_OPEN_CLOSE_CHAT": "Apri/Chiudi Chat",
"MESSAGE_SPEAKER": "Speaker",
"MESSAGE_POPUP": "Popup",
"CHAT_TITLE_MEMBERS": "Membri",
"CHAT_TITLE": "Chat",
"CHAT_NO_MEMBERS": "Non ci sono membri da mostrare.",
"CHAT_GENERAL": "Generale",
"CHAT_TITLE_KICK": "Kick",
"CHAT_KICK": "Kick",
"CHAT_TITLE_VIDEO_FLOOR": "Video Floor",
"CHAT_FLOOR": "Floor",
"CHAT_TITLE_TRASFER": "Transfer",
"CHAT_TRANSFER": "Transfer",
"CHAT_BANNER": "Banner",
"CHAT_TITLE_SET": "Set",
"CHAT_SET": "Set",
"CHAT_TITLE_RESET": "Reset",
"CHAT_RESET": "Reset",
"CHAT_RESET": "Reset",
"CHAT_CANVAS": "Canvas",
"CHAT_CANVAS_IN": "Canvas In",
"CHAT_CANVAS_OUT": "Canvas Out",
"CHAT_PREV": "Precedente",
"CHAT_NEXT": "Successivo",
"CHAT_LAYER": "Layer",
"CHAT_AUDIO_VIDEO": "Audio/Video",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Mute/Unmute Mic",
"CHAT_MUTE_MIC": "Mute",
"CHAT_UNMUTE_MIC": "Unmute",
"CHAT_TITLE_MUTE_UNMUTE_VIDEO": "Mute/Unmute Video",
"CHAT_NO_MESSAGES": "Non ci sono messaggi da mostrare.",
"CHAT_SEND_MESSAGE": "Invia",
"CHAT_TYPE_MESSAGE": "Scrivi il tuo messaggio qui...",
"TITLE_CONTRIBUTORS": "Contributori",
"MESSAGE_CONNECTION_UNTRUSTED": "Questa connessione non è sicura.",
"MESSAGE_TOGGLE_NAVIGATION": "Abilita/Disabilita navigazione",
"BANDWIDTH_INFO": "Informazioni sulla larghezza di banda",
"BANDWIDTH_INFO_INCOMING": "Ingresso:",
"BANDWIDTH_INFO_OUTGOING": "Uscita:",
"BANDWIDTH_INFO_VIDEO_RES": "Risoluzione Video:",
"IN_CALL": "In chiamata: ",
"LAST_CALL": "Ultima chiamata: ",
"OPEN_NEW_WINDOW": "Apri Una Nuova Finestra",
"CHANGE_LOGIN_INFO": "Cambia le informazioni di login",
"LOGOUT": "Logout",
"ABOUT": "About",
"HELP": "Aiuto",
"CONTRIBUTORS": "Contributori",
"TITLE_PREVIEW_SETTINGS": "Configura le impostazioni della tua video camera e del tuo microfono",
"CAMERA_SETTINGS": "Video Camera:",
"MIC_SETTINGS": "Microfono:",
"SAVE": "Salva",
"LOADING": "Caricamento",
"ERRORS" : "Errori",
"CALLING_TO": "Chiamata verso ",
"CANCELLING": "In annullamento",
"DETERMINING_SPEED": "Calcolo della tua velocità...",
"CALL_HISTORY": "Cronologia Chiamate",
"CLEAR_CALL_HISTORY": "Rimuovi la cronologia",
"NO_CALL_HISTORY": "Nessuna chiamata nella cronologia.",
"ENTER_EXTENSION": "Inserisci un numero",
"CALL_EXTENSION": "Chiama il numero",
"LOGIN": "Login",
"LOGIN_INFORMATION": "Informazioni di login",
"SAVE_LOGIN_INFORMATION": "Salva le informazioni di login",
"INVALID_LOGIN_FIELDS": "Verifica i campi e prova di nuovo.",
"NAME": "Nome",
"YOUR_NAME": "Il tuo nome",
"EMAIL": "Email",
"YOUR_EMAIL": "Il tuo indirizzo email",
"USER": "Utente",
"PASSWORD": "Password",
"CALLER_ID": "Caller ID",
"HOSTNAME": "Hostname",
"WEBSOCKET_URL": "Websocket URL",
"SETTINGS": "Impostazioni",
"DEVICE_SETTINGS": "Configurazione dei dispositivi",
"SHARE_DEVICE": "Dispositivo in condivisione",
"SPEAKER": "Altoparlante:",
"SPEAKER_FEATURE": "Il tuo browser sembra non supportare questa funzionalità",
"PREVIEW_SETTINGS": "Anteprima delle configurazioni",
"REFRESH_DEVICE_LIST": "Aggiorna la lista dei dispositivi",
"GENERAL_SETTINGS": "Configurazioni generali:",
"USE_VIDEO": "Abilita Video",
"USE_STEREO_AUDIO": "Abilita Audio Stereo",
"USE_STUN": "Abilita STUN",
"SCALE_VIDEO": "Scala il video remoto con la risoluzione della video camera",
"ASK_BEFORE_RECOVER": "Chiedi prima di recuperare una chiamata",
"BEST_FRAME_RATE": "Miglior frame rate:",
"AUDIO_SETTINGS": "Impostazioni audio:",
"ECHO_CANCEL": "Cancellatore d'eco",
"NOISE_SUPPRESSION": "Soppressione del rumore",
"HIGHPASS_FILTER": "Highpass Filter",
"VIDEO_SETTINGS": "Impostazioni video:",
"REMOTE_ENCODER": "Abilita codificatore remoto dedicato.",
"AUTO_SPEED_RES": "Rileva in modo automatico la velocità e le impostazioni",
"RECHECK_BANDWIDTH": "Controlla la larghezza di banda per ogni chiamata in uscita",
"CHECK_NETWORK_SPEED": "Controllo della velocità di rete",
"VIDEO_QUALITY": "Qualità video:",
"MAX_INCOMING_BANDWIDTH": "Massima larghezza di banda in ingresso:",
"MAX_OUTGOING_BANDWIDTH": "Massima larghezza di banda in uscita:",
"FACTORY_RESET": "Reset ai valori di default",
"SAVE_DEVICE_SETTINGS": "Salva le impostazioni dei dispositivi",
"BROWSER_COMPATIBILITY": "Verifica compatibilità browser.",
"REFRESH_MEDIA_DEVICES": "Aggiornamento dei dispositivi.",
"BROWSER_WITHOUT_WEBRTC": "Errore: il browser non supporta WebRTC.",
"CHECK_PERMISSION_MEDIA": "Verifica permessi dispositivi",
"CHECK_PROVISIONING_CONF": "Recupero della configurazione.",
"CHECK_LOGIN": "Verifica del login.",
"CHECK_CONNECTION_SPEED": "Verifica velocità connessione.",
"ERROR_PERMISSION_MEDIA": "Errore: permesso sui dispositivi negato",
"ERROR_PROVISIONING_CONF": "Errore: Recupero configurazione fallito.",
"PLEASE_WAIT": "Attendere prego...",
"CANCEL": "Cancella",
"CHAT_TITLE_VOL_MINUS": "Volume -",
"CHAT_TITLE_VOL_PLUS": "Volume +",
"CHAT_TITLE_GAIN_MINUS": "Guadagno -",
"CHAT_TITLE_GAIN_PLUS": "Guadagno +",
"CHAT_VOL_MINUS": "Vol -",
"CHAT_VOL_PLUS": "Vol +",
"CHAT_GAIN_MINUS": "Guadagno -",
"CHAT_GAIN_PLUS": "Guadagno +"
}

View File

@ -0,0 +1,151 @@
{
"TITLE_ACTIVE_CALL": "Ops, já existe uma chamada ativa.",
"MESSAGE_ACTIVE_CALL_HANGUP": "Parece que você está em uma chamada. Você quer desligar?",
"MESSAGE_ACTIVE_CALL_BACK": "Parece que você estava em uma chamada antes de sair pela última vez. Quer voltar pra ela?",
"TITLE_INCOMING_CALL": "Nova Chamada",
"MESSAGE_INCOMING_CALL": "de ",
"MESSAGE_NO_HANGUP_CALL": "Não há chamada para desligar.",
"MESSAGE_ENTER_FILENAME": "Por favor, insira o nome do arquivo",
"TITLE_ENABLE_VIDEO": "Deseja ativar o vídeo para esta chamada?",
"MESSAGE_ENABLE_VIDEO": "O vídeo será ativado para as próximas chamadas.",
"TITLE_INSERT_BANNER": "Por favor, insira o texto do banner",
"TITLE_INSERT_CANVAS_ID": "Por favor, insira o ID do Canvas",
"TITLE_INSERT_LAYER": "Por favor, insira o Layer",
"TITLE_TRANSFER": "Transferir?",
"MESSAGE_TRANSFER": "Para qual destino você deseja transferir esta chamada?",
"LABEL_TRANSFER": "Destino",
"MESSAGE_DISPLAY_SETTINGS": "Não é possível mostrar a pré-visualização das configurações durante uma chamda",
"BUTTON_END_CALL": "Finalizar Chamada",
"BUTTON_CLOSE": "Fechar",
"MESSAGE_PLAY": "Reproduzir",
"MESSAGE_STOP": "Parar",
"MESSAGE_RECORD": "Gravar",
"MESSAGE_STOP_RECORD": "Parar Gravação",
"MESSAGE_SNAPSHOT": "Snapshot",
"MESSAGE_VIDEO_MODE": "Modo de Vídeo",
"MESSAGE_MUTE_MIC": "Ativar/Desativar Mudo do Microfone",
"MESSAGE_MUTE_VIDEO": "Ativar/Desativar Mudo do Vídeo",
"MESSAGE_FULLSCREEN": "Ativar/Desativar Modo Tela Cheia",
"MESSAGE_SCREENSHARE": "Compartilhar Tela",
"MESSAGE_OPEN_CLOSE_CHAT": "Abrir/Fechar Chat",
"MESSAGE_SPEAKER": "Alto-falante",
"MESSAGE_POPUP": "Popup",
"CHAT_TITLE_MEMBERS": "Membros",
"CHAT_TITLE": "Chat",
"CHAT_NO_MEMBERS": "Não há membros para mostrar.",
"CHAT_GENERAL": "Geral",
"CHAT_TITLE_KICK": "Chutar",
"CHAT_KICK": "Chutar",
"CHAT_TITLE_VIDEO_FLOOR": "Floor do Vídeo",
"CHAT_FLOOR": "Floor",
"CHAT_TITLE_TRANSFER": "Transferência",
"CHAT_TRANSFER": "Transferir",
"CHAT_BANNER": "Banner",
"CHAT_TITLE_SET": "Definir",
"CHAT_SET": "Definir",
"CHAT_TITLE_RESET": "Redefinir",
"CHAT_RESET": "Redefinir",
"CHAT_CANVAS": "Canvas",
"CHAT_CANVAS_IN": "Colocar no Canvas",
"CHAT_CANVAS_OUT": "Remover do Canvas",
"CHAT_PREV": "Anterior",
"CHAT_NEXT": "Seguinte",
"CHAT_LAYER": "Layer",
"CHAT_AUDIO_VIDEO": "Áudio/Vídeo",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Ativar/Desativar Microfone",
"CHAT_MUTE_MIC": "Ativar Mudo",
"CHAT_UNMUTE_MIC": "Desativar Mudo",
"CHAT_TITLE_MUTE_UNMUTE_VIDEO": "Ativar/Desativar Vídeo",
"CHAT_NO_MESSAGES": "Não há mensagens para mostrar.",
"CHAT_SEND_MESSAGE": "Enviar",
"CHAT_TYPE_MESSAGE": "Escreva sua mensagem aqui...",
"TITLE_CONTRIBUTORS": "Contribuidores",
"MESSAGE_CONNECTION_UNTRUSTED": "Esta conexão não é confiável.",
"MESSAGE_TOGGLE_NAVIGATION": "Ativar/Desativar Navegação",
"BANDWIDTH_INFO": "Informações sobre largura de banda",
"BANDWIDTH_INFO_INCOMING": "Entrada:",
"BANDWIDTH_INFO_OUTGOING": "Saída:",
"BANDWIDTH_INFO_VIDEO_RES": "Resolução de vídeo:",
"IN_CALL": "Em chamada:",
"LAST_CALL": "Última chamada:",
"OPEN_NEW_WINDOW": "Abrir nova janela",
"CHANGE_LOGIN_INFO": "Mudar informações de login",
"LOGOUT": "Sair",
"ABOUT": "Sobre",
"HELP": "Ajuda",
"CONTRIBUTORS": "Contribuidores",
"TITLE_PREVIEW_SETTINGS": "Defina suas configurações de câmera e microfone",
"CAMERA_SETTINGS": "Câmera:",
"MIC_SETTINGS": "Microfone:",
"SAVE": "Salvar",
"LOADING": "Carregando",
"ERRORS" : "Erros",
"CALLING_TO": "Ligando para ",
"CANCELLING": "Cancelando...",
"DETERMINING_SPEED": "Determinando sua velocidade...",
"CALL_HISTORY": "Histórico de Chamadas",
"CLEAR_CALL_HISTORY": "Limpar Histórico",
"NO_CALL_HISTORY": "Não há chamadas no histórico.",
"ENTER_EXTENSION": "Insira um número",
"CALL_EXTENSION": "Ligar para número",
"LOGIN": "Login",
"LOGIN_INFORMATION": "Informações de login",
"SAVE_LOGIN_INFORMATION": "Salvar informações de login",
"INVALID_LOGIN_FIELDS": "Verifique os campos abaixo e tente novamente.",
"NAME": "Nome",
"YOUR_NAME": "Seu nome",
"EMAIL": "E-mail",
"YOUR_EMAIL": "Seu e-mail",
"USER": "Usuário",
"PASSWORD": "Senha",
"CALLER_ID": "Identificação de chamada",
"HOSTNAME": "Servidor",
"WEBSOCKET_URL": "Endereço do websocket",
"SETTINGS": "Configurações",
"DEVICE_SETTINGS": "Configurações de dispositivos",
"SHARE_DEVICE": "Dispositivo de compartilhamento",
"SPEAKER": "Alto-falante:",
"SPEAKER_FEATURE": "Seu navegador parece não oferecer suporte a esta funcionalidade",
"PREVIEW_SETTINGS": "Pré-visualizar configurações",
"REFRESH_DEVICE_LIST": "Atualizar lista de dispositivos",
"GENERAL_SETTINGS": "Configurações gerais:",
"USE_VIDEO": "Habilitar Vídeo",
"USE_STEREO_AUDIO": "Áudio Estéreo",
"USE_STUN": "Habilitar STUN",
"SCALE_VIDEO": "Redimensionar o vídeo remoto para bater com a resolução da câmera",
"ASK_BEFORE_RECOVER": "Perguntar antes de recuperar uma chamada",
"BEST_FRAME_RATE": "Melhor frame rate:",
"AUDIO_SETTINGS": "Configurações de áudio:",
"ECHO_CANCEL": "Cancelamento de eco",
"NOISE_SUPPRESSION": "Supressão de ruído",
"HIGHPASS_FILTER": "Filtro passa-alta",
"VIDEO_SETTINGS": "Configurações de vídeo:",
"REMOTE_ENCODER": "Encoder Remoto Dedicado habilitado.",
"AUTO_SPEED_RES": "Determinar automaticamente velocidade e configurações de resolução",
"RECHECK_BANDWIDTH": "Verificar novamente largura de banda antes de realizar cada chamada",
"CHECK_NETWORK_SPEED": "Verificar velocidade da rede",
"VIDEO_QUALITY": "Qualidade do vídeo:",
"MAX_INCOMING_BANDWIDTH": "Largura de banda de entrada máxima:",
"MAX_OUTGOING_BANDWIDTH": "Largura de banda de saída máxima:",
"FACTORY_RESET": "Restaurar padrões de fábrica",
"SAVE_DEVICE_SETTINGS": "Salvar configurações de dispositivos",
"BROWSER_COMPATIBILITY": "Verificando compatibilidade do browser.",
"REFRESH_MEDIA_DEVICES": "Atualizando dispositivos de mídia.",
"BROWSER_WITHOUT_WEBRTC": "Erro: navegador não oferece suporte ao WebRTC.",
"CHECK_PERMISSION_MEDIA": "Verificando permissões de mídia",
"CHECK_PROVISIONING_CONF": "Provisionando configurações.",
"CHECK_LOGIN": "Verificando login.",
"CHECK_CONNECTION_SPEED": "Verificando velocidade de conexão.",
"ERROR_PERMISSION_MEDIA": "Erro: permissão de mídia negada.",
"ERROR_PROVISIONING_CONF": "Erro: provisionamento falhou.",
"PLEASE_WAIT": "Por favor, aguarde...",
"CANCEL": "Cancelar",
"CHAT_TITLE_VOL_MINUS": "Volume -",
"CHAT_TITLE_VOL_PLUS": "Volume +",
"CHAT_TITLE_GAIN_MINUS": "Ganho -",
"CHAT_TITLE_GAIN_PLUS": "Ganho +",
"CHAT_VOL_MINUS": "Vol -",
"CHAT_VOL_PLUS": "Vol +",
"CHAT_GAIN_MINUS": "Ganho -",
"CHAT_GAIN_PLUS": "Ganho +"
}

View File

@ -0,0 +1,151 @@
{
"TITLE_ACTIVE_CALL": "Упс, Уже имеется активный вызов.",
"MESSAGE_ACTIVE_CALL_HANGUP": "Такое впечатление, что вы уже разговариваете. Хоитите ли Вы положить трубку?",
"MESSAGE_ACTIVE_CALL_BACK": "Такое впечатление что Вы разговаривали когда отключились в прошлый раз. Хотите ли Вы вернутся к предыдущему собеседнику?",
"TITLE_INCOMING_CALL": "Входящий вызов",
"MESSAGE_INCOMING_CALL": "от ",
"MESSAGE_NO_HANGUP_CALL": "Отсутствуют вызовы которые можно завершить.",
"MESSAGE_ENTER_FILENAME": "Пожалуйста, укажите имя файла",
"TITLE_ENABLE_VIDEO": "Хотите ли Вы начать передачу видео для этого звонка?",
"MESSAGE_ENABLE_VIDEO": "Для следующего звонка включена передача видео.",
"TITLE_INSERT_BANNER": "Пожалуйста укажите текст заголовка",
"TITLE_INSERT_CANVAS_ID": "Пожалуйста укажите идентификатор канвы",
"TITLE_INSERT_LAYER": "Пожалуйста укажите слой",
"TITLE_TRANSFER": "Перевести вызов?",
"MESSAGE_TRANSFER": "На какой номер Вы хотите перевести этот вызов?",
"LABEL_TRANSFER": "Вызываемый номер",
"MESSAGE_DISPLAY_SETTINGS": "Не могу отобразить параметры предпросмотра во время вызова",
"BUTTON_END_CALL": "Завершить вызов",
"BUTTON_CLOSE": "Закрыть",
"MESSAGE_PLAY": "Проиграть",
"MESSAGE_STOP": "Остановить",
"MESSAGE_RECORD": "Записать",
"MESSAGE_STOP_RECORD": "Остановить запись",
"MESSAGE_SNAPSHOT": "Снимок экрана",
"MESSAGE_VIDEO_MODE": "Видеорежим",
"MESSAGE_MUTE_MIC": "вкл./выкл. Микрофон",
"MESSAGE_MUTE_VIDEO": "вкл./выкл. Камеру",
"MESSAGE_FULLSCREEN": "Переключить полноэкранный режим",
"MESSAGE_SCREENSHARE": "Дать доступ к рабочему столу",
"MESSAGE_OPEN_CLOSE_CHAT": "Открыть/Закрыть чат",
"MESSAGE_SPEAKER": "Динамик",
"MESSAGE_POPUP": "Всплывающее сообщение",
"CHAT_TITLE_MEMBERS": "Участники",
"CHAT_TITLE": "Чат",
"CHAT_NO_MEMBERS": "Нет участников.",
"CHAT_GENERAL": "Общая",
"CHAT_TITLE_KICK": "Выкинуть",
"CHAT_KICK": "Выкинуть",
"CHAT_TITLE_VIDEO_FLOOR": "Видео мин.уровень",
"CHAT_FLOOR": "Мин.уровень",
"CHAT_TITLE_TRANSFER": "Перевести",
"CHAT_TRANSFER": "Перевести",
"CHAT_BANNER": "Заголовок",
"CHAT_TITLE_SET": "Установить",
"CHAT_SET": "Установить",
"CHAT_TITLE_RESET": "Сбросить",
"CHAT_RESET": "Сбросить",
"CHAT_CANVAS": "Канва",
"CHAT_CANVAS_IN": "Канва при входе",
"CHAT_CANVAS_OUT": "Канва при выходе",
"CHAT_PREV": "Предыдущий",
"CHAT_NEXT": "Следующий",
"CHAT_LAYER": "Слой",
"CHAT_AUDIO_VIDEO": "Аудио/Видео",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Вкл./Выкл. мик.",
"CHAT_MUTE_MIC": "Выкл.",
"CHAT_UNMUTE_MIC": "Вкл.",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Вкл./Выкл. видео",
"CHAT_NO_MESSAGES": "Нет сообщений для отображения.",
"CHAT_SEND_MESSAGE": "Отправить",
"CHAT_TYPE_MESSAGE": "Наберите ваше сообщение тут...",
"TITLE_CONTRIBUTORS": "Помощники",
"MESSAGE_CONNECTION_UNTRUSTED": "Данное соединение не доверенное.",
"MESSAGE_TOGGLE_NAVIGATION": "Переключить навигацию",
"BANDWIDTH_INFO": "Информация о полосе пропускания",
"BANDWIDTH_INFO_INCOMING": "На прием:",
"BANDWIDTH_INFO_OUTGOING": "При передаче:",
"BANDWIDTH_INFO_VIDEO_RES": "Видео разрешение:",
"IN_CALL": "Разговор с:",
"LAST_CALL": "Предыдущий вызов:",
"OPEN_NEW_WINDOW": "Открыть новое окно",
"CHANGE_LOGIN_INFO": "Изменить информацию о логине",
"LOGOUT": "Выйти",
"ABOUT": "О программе",
"HELP": "Помощь",
"CONTRIBUTORS": "Помощники",
"TITLE_PREVIEW_SETTINGS": "Настройте камеру и параметры микрофона",
"CAMERA_SETTINGS": "Камера:",
"MIC_SETTINGS": "Микрофон:",
"SAVE": "Сохранить",
"LOADING": "Загружаю",
"ERRORS" : "Ошибки",
"CALLING_TO": "Вызываю ",
"CANCELLING": "Завершаю...",
"DETERMINING_SPEED": "Определяю скорость подключения ...",
"CALL_HISTORY": "История вызовов",
"CLEAR_CALL_HISTORY": "Очистить историю",
"NO_CALL_HISTORY": "История вызов пуста.",
"ENTER_EXTENSION": "Укажите вызываемый номер",
"CALL_EXTENSION": "Набрать внутрений номер",
"LOGIN": "Логин",
"LOGIN_INFORMATION": "Информация о логине",
"SAVE_LOGIN_INFORMATION": "Сохранить информацию о логине",
"INVALID_LOGIN_FIELDS": "Проверьте поля указанные ниже и повторите снова.",
"NAME": "Имя",
"YOUR_NAME": "Ваше имя",
"EMAIL": "Почта",
"YOUR_EMAIL": "Ваша почта",
"USER": "Пользователь",
"PASSWORD": "Пароль",
"CALLER_ID": "АОН",
"HOSTNAME": "Имя хоста",
"WEBSOCKET_URL": "URL вебсокета",
"SETTINGS": "Параметры",
"DEVICE_SETTINGS": "Параметры устройства",
"SHARE_DEVICE": "Дать доступ к устройству",
"SPEAKER": "Динамик:",
"SPEAKER_FEATURE": "Вероятно Ваш браузер не поддерживают это функцию",
"PREVIEW_SETTINGS": "Параметры предпросмотра",
"REFRESH_DEVICE_LIST": "Обновить список устройств",
"GENERAL_SETTINGS": "Основные параметры:",
"USE_VIDEO": "Использовать видео",
"USE_STEREO_AUDIO": "Стереозвук",
"USE_STUN": "Использовать STUN",
"SCALE_VIDEO": "Маштабировать удаленное видео так чтобы соответствовать разрешению камеры",
"ASK_BEFORE_RECOVER": "Спросить перед востановлением вызова",
"BEST_FRAME_RATE": "Оптимальная частота кадров:",
"AUDIO_SETTINGS": "Параметры звука:",
"ECHO_CANCEL": "Эхо компенсация",
"NOISE_SUPPRESSION": "Шумоподавление",
"HIGHPASS_FILTER": "Высокочастотный фильтр",
"VIDEO_SETTINGS": "Параметры видео:",
"REMOTE_ENCODER": "Включен специализированный удаленный энкодер.",
"AUTO_SPEED_RES": "Автоматически определять скорость подключения и параметры разрешения",
"RECHECK_BANDWIDTH": "Определять полосу пропускания перед каждым исходящим вызовом",
"CHECK_NETWORK_SPEED": "Проверить скорость сети",
"VIDEO_QUALITY": "Качество видео:",
"MAX_INCOMING_BANDWIDTH": "Макс. полоса пропускания на вход:",
"MAX_OUTGOING_BANDWIDTH": "Макс. полоса пропускания на выход:",
"FACTORY_RESET": "Сброс к заводским настройкам",
"SAVE_DEVICE_SETTINGS": "Сохранить параметры устройства",
"BROWSER_COMPATIBILITY": "Проверяю возможности браузера.",
"REFRESH_MEDIA_DEVICES": "Обновить список медиа-устройств.",
"BROWSER_WITHOUT_WEBRTC": "Ошибка: браузер не поддерживает WebRTC.",
"CHECK_PERMISSION_MEDIA": "Проверю разрешения доступа к медиа",
"CHECK_PROVISIONING_CONF": "Подготовка конфигурации.",
"CHECK_LOGIN": "Проверка логина.",
"CHECK_CONNECTION_SPEED": "Проверка скорости подключения.",
"ERROR_PERMISSION_MEDIA": "Ошибка: Отказано в доступе к медиа-устройствам",
"ERROR_PROVISIONING_CONF": "Ошибка: Подготовить неудалось.",
"PLEASE_WAIT": "Пожалуйста подождите...",
"CANCEL": "Завершить",
"CHAT_TITLE_VOL_MINUS": "Громкость -",
"CHAT_TITLE_VOL_PLUS": "Громкость +",
"CHAT_TITLE_GAIN_MINUS": "Усиление -",
"CHAT_TITLE_GAIN_PLUS": "Усиление +",
"CHAT_VOL_MINUS": "Гром. -",
"CHAT_VOL_PLUS": "Гром. +",
"CHAT_GAIN_MINUS": "Усил. -",
"CHAT_GAIN_PLUS": "Усил. +"
}

View File

@ -0,0 +1,151 @@
{
"TITLE_ACTIVE_CALL": "Hoppsan, Aktivt samtal pågår.",
"MESSAGE_ACTIVE_CALL_HANGUP": "Det verkar som du är i ett samtal. Vill du lägga på?",
"MESSAGE_ACTIVE_CALL_BACK": "Det verkar som du var i ett samtal innan du lämnade senast, vill du gå tillbaka till det?",
"TITLE_INCOMING_CALL": "Inkommande Samtal",
"MESSAGE_INCOMING_CALL": "från ",
"MESSAGE_NO_HANGUP_CALL": "Det finns inget samtal att avsluta.",
"MESSAGE_ENTER_FILENAME": "Ange filnamn",
"TITLE_ENABLE_VIDEO": "Vill du aktivera video i pågående samtal?",
"MESSAGE_ENABLE_VIDEO": "Video kommer aktiveras i efterföljande samtal.",
"TITLE_INSERT_BANNER": "Lägg till banner text",
"TITLE_INSERT_CANVAS_ID": "Lägg till Canvas Id",
"TITLE_INSERT_LAYER": "Lägg till Lager",
"TITLE_TRANSFER": "Koppla samtal?",
"MESSAGE_TRANSFER": "Vilken destination vill du koppla samtalet till?",
"LABEL_TRANSFER": "Destination",
"MESSAGE_DISPLAY_SETTINGS": "Kan inte förhandsvisa inställningar under samtal",
"BUTTON_END_CALL": "Avsluta samtal",
"BUTTON_CLOSE": "Stäng",
"MESSAGE_PLAY": "Spela upp",
"MESSAGE_STOP": "Stoppa",
"MESSAGE_RECORD": "Spela in",
"MESSAGE_STOP_RECORD": "Avsluta inspelning",
"MESSAGE_SNAPSHOT": "Snapshot",
"MESSAGE_VIDEO_MODE": "Videoläge",
"MESSAGE_MUTE_MIC": "(av)Aktivera Mikrofon",
"MESSAGE_MUTE_VIDEO": "(av)Aktivera Video",
"MESSAGE_FULLSCREEN": "Växla fullskärmsläge",
"MESSAGE_SCREENSHARE": "Skärmdelning",
"MESSAGE_OPEN_CLOSE_CHAT": "Öppna/stäng chatt",
"MESSAGE_SPEAKER": "Högtalare",
"MESSAGE_POPUP": "Popup",
"CHAT_TITLE_MEMBERS": "Medlemmar",
"CHAT_TITLE": "Chatt",
"CHAT_NO_MEMBERS": "Det finns inga medlemmar att visa.",
"CHAT_GENERAL": "Allmänt",
"CHAT_TITLE_KICK": "Sparka ut",
"CHAT_KICK": "Sparka ut",
"CHAT_TITLE_VIDEO_FLOOR": "Video Floor",
"CHAT_FLOOR": "Floor",
"CHAT_TITLE_TRANSFER": "Koppla",
"CHAT_TRANSFER": "Koppla",
"CHAT_BANNER": "Banner",
"CHAT_TITLE_SET": "Sätt",
"CHAT_SET": "Sätt",
"CHAT_TITLE_RESET": "Reset",
"CHAT_RESET": "Reset",
"CHAT_CANVAS": "Canvas",
"CHAT_CANVAS_IN": "Canvas In",
"CHAT_CANVAS_OUT": "Canvas Ut",
"CHAT_PREV": "Föregående",
"CHAT_NEXT": "Nästa",
"CHAT_LAYER": "Lager",
"CHAT_AUDIO_VIDEO": "Ljud/Video",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "Aktivera/Inaktivera Mikrofon",
"CHAT_MUTE_MIC": "Inaktivera",
"CHAT_UNMUTE_MIC": "Aktivera",
"CHAT_TITLE_MUTE_UNMUTE_VIDEO": "Aktivera/Inaktivera Video",
"CHAT_NO_MESSAGES": "Det finns inga meddelanden att visa.",
"CHAT_SEND_MESSAGE": "Skicka",
"CHAT_TYPE_MESSAGE": "Skriv ditt meddelande här...",
"TITLE_CONTRIBUTORS": "Contributors",
"MESSAGE_CONNECTION_UNTRUSTED": "Denna anslutning är opålitlig.",
"MESSAGE_TOGGLE_NAVIGATION": "Toggla navigation",
"BANDWIDTH_INFO": "Bandbreddsinformation",
"BANDWIDTH_INFO_INCOMING": "Inkommande:",
"BANDWIDTH_INFO_OUTGOING": "Utgående:",
"BANDWIDTH_INFO_VIDEO_RES": "Videoupplösning:",
"IN_CALL": "I Samtal:",
"LAST_CALL": "Senaste Samtal:",
"OPEN_NEW_WINDOW": "Öppna nytt fönster",
"CHANGE_LOGIN_INFO": "Ändra inloggningsinformation",
"LOGOUT": "Logga ut",
"ABOUT": "Om",
"HELP": "Hjälp",
"CONTRIBUTORS": "Contributors",
"TITLE_PREVIEW_SETTINGS": "Inställningar för kamera och mikrofon",
"CAMERA_SETTINGS": "Kamera:",
"MIC_SETTINGS": "Mikrofon:",
"SAVE": "Spara",
"LOADING": "Laddar",
"ERRORS" : "Fel",
"CALLING_TO": "Ringer till ",
"CANCELLING": "Avbryter...",
"DETERMINING_SPEED": "Avgör din hastighet...",
"CALL_HISTORY": "Samtalshistorik",
"CLEAR_CALL_HISTORY": "Rensa Historik",
"NO_CALL_HISTORY": "Ingen samtalshistorik.",
"ENTER_EXTENSION": "Ange anknytning",
"CALL_EXTENSION": "Ring Anknytning",
"LOGIN": "Inloggning",
"LOGIN_INFORMATION": "Inloggningsinformation",
"SAVE_LOGIN_INFORMATION": "Spara inloggningsinformation",
"INVALID_LOGIN_FIELDS": "Verifiera nedanstående fält och försök igen.",
"NAME": "Namn",
"YOUR_NAME": "Ditt namn",
"EMAIL": "Epost",
"YOUR_EMAIL": "Din epost",
"USER": "Användare",
"PASSWORD": "Lösenord",
"CALLER_ID": "Utringande ID",
"HOSTNAME": "Servernamn",
"WEBSOCKET_URL": "Websocket URL",
"SETTINGS": "Inställningar",
"DEVICE_SETTINGS": "Enhetsinställningar",
"SHARE_DEVICE": "Dela enhet",
"SPEAKER": "Högtalare:",
"SPEAKER_FEATURE": "Din browser verkar inte stödja denna funktion",
"PREVIEW_SETTINGS": "Förhandsgranska inställningar",
"REFRESH_DEVICE_LIST": "Uppdatera enhetslista",
"GENERAL_SETTINGS": "Generella inställningar:",
"USE_VIDEO": "Använd Video",
"USE_STEREO_AUDIO": "Stereoljud",
"USE_STUN": "Använd STUN",
"SCALE_VIDEO": "Skala fjärrvideo för att matcha kameraupplösning",
"ASK_BEFORE_RECOVER": "Fråga före återhämtning av samtal",
"BEST_FRAME_RATE": "Bästa framerate:",
"AUDIO_SETTINGS": "Ljudinställningar:",
"ECHO_CANCEL": "Echo Cancellation",
"NOISE_SUPPRESSION": "Brusreducering",
"HIGHPASS_FILTER": "Högpassfilter",
"VIDEO_SETTINGS": "Videoinställningar:",
"REMOTE_ENCODER": "Dedikerad Remote Encoder aktiverad.",
"AUTO_SPEED_RES": "Automatiska inställningar för hastighet och upplösning",
"RECHECK_BANDWIDTH": "Kontrollera bandbredd före varje utgående samtal",
"CHECK_NETWORK_SPEED": "Kontrollera nätverkshastighet",
"VIDEO_QUALITY": "Videokvalitet:",
"MAX_INCOMING_BANDWIDTH": "Max inkommande bandbredd:",
"MAX_OUTGOING_BANDWIDTH": "Max utgående bandbredd:",
"FACTORY_RESET": "Fabriksåterställning",
"SAVE_DEVICE_SETTINGS": "Spara inställningar",
"BROWSER_COMPATIBILITY": "Kontrollerar browserkompatibilitet.",
"REFRESH_MEDIA_DEVICES": "Uppdatera mediaenheter.",
"BROWSER_WITHOUT_WEBRTC": "Fel: browsern saknar stöd för WebRTC.",
"CHECK_PERMISSION_MEDIA": "Kontrollerar mediarättigheter",
"CHECK_PROVISIONING_CONF": "Provisioneringskonfiguration.",
"CHECK_LOGIN": "Kontrollerar inloggning.",
"CHECK_CONNECTION_SPEED": "Kontrollera anslutningshastighet.",
"ERROR_PERMISSION_MEDIA": "Fel: Mediaåtkomst nekad",
"ERROR_PROVISIONING_CONF": "Fel: Provisionering misslyckades.",
"PLEASE_WAIT": "Vänligen vänta...",
"CANCEL": "Avbryt",
"CHAT_TITLE_VOL_MINUS": "Volym -",
"CHAT_TITLE_VOL_PLUS": "Volym +",
"CHAT_TITLE_GAIN_MINUS": "Förstärkning -",
"CHAT_TITLE_GAIN_PLUS": "Förstärkning +",
"CHAT_VOL_MINUS": "Vol -",
"CHAT_VOL_PLUS": "Vol +",
"CHAT_GAIN_MINUS": "Förstärkning -",
"CHAT_GAIN_PLUS": "Förstärkning +"
}

View File

@ -0,0 +1,151 @@
{
"TITLE_ACTIVE_CALL": "囧,正在通话中",
"MESSAGE_ACTIVE_CALL_HANGUP": "好像正在通话中,你想挂断吗?",
"MESSAGE_ACTIVE_CALL_BACK": "好像上次还有个电话没有挂断?你想恢复上次的通话吗?",
"TITLE_INCOMING_CALL": "新来电",
"MESSAGE_INCOMING_CALL": "来自 ",
"MESSAGE_NO_HANGUP_CALL": "没有可挂断的电话",
"MESSAGE_ENTER_FILENAME": "请输入文件名",
"TITLE_ENABLE_VIDEO": "你想为当前通话开启视频吗?",
"MESSAGE_ENABLE_VIDEO": "开启视频将在下次通话生效",
"TITLE_INSERT_BANNER": "请输入标题文本",
"TITLE_INSERT_CANVAS_ID": "请选择一个画布ID",
"TITLE_INSERT_LAYER": "请输入一个层",
"TITLE_TRANSFER": "转移方?",
"MESSAGE_TRANSFER": "你想把该电话转移到什么号码?",
"LABEL_TRANSFER": "目的地",
"MESSAGE_DISPLAY_SETTINGS": "通话中不能预览",
"BUTTON_END_CALL": "挂断",
"BUTTON_CLOSE": "关闭",
"MESSAGE_PLAY": "播放",
"MESSAGE_STOP": "停止",
"MESSAGE_RECORD": "录音/录像",
"MESSAGE_STOP_RECORD": "停止录音/录像",
"MESSAGE_SNAPSHOT": "抓拍",
"MESSAGE_VIDEO_MODE": "停止发送视频",
"MESSAGE_MUTE_MIC": "静音/恢复",
"MESSAGE_MUTE_VIDEO": "停止视频/恢复",
"MESSAGE_FULLSCREEN": "全屏",
"MESSAGE_SCREENSHARE": "屏幕共享",
"MESSAGE_OPEN_CLOSE_CHAT": "打开/关闭聊天",
"MESSAGE_SPEAKER": "喇叭",
"MESSAGE_POPUP": "弹出",
"CHAT_TITLE_MEMBERS": "成员",
"CHAT_TITLE": "聊天",
"CHAT_NO_MEMBERS": "没有成员",
"CHAT_GENERAL": "一般",
"CHAT_TITLE_KICK": "踢出",
"CHAT_KICK": "踢出",
"CHAT_TITLE_VIDEO_FLOOR": "视频Floor",
"CHAT_FLOOR": "Floor",
"CHAT_TITLE_TRANSFER": "转移",
"CHAT_TRANSFER": "转移",
"CHAT_BANNER": "标题",
"CHAT_TITLE_SET": "设置",
"CHAT_SET": "设置",
"CHAT_TITLE_RESET": "重置",
"CHAT_RESET": "重置",
"CHAT_CANVAS": "画布",
"CHAT_CANVAS_IN": "入画布",
"CHAT_CANVAS_OUT": "出画布",
"CHAT_PREV": "上一个",
"CHAT_NEXT": "下一个",
"CHAT_LAYER": "层",
"CHAT_AUDIO_VIDEO": "音频/视频",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "静音/恢复",
"CHAT_MUTE_MIC": "静音",
"CHAT_UNMUTE_MIC": "恢复",
"CHAT_TITLE_MUTE_UNMUTE_MIC": "停止/恢复视频",
"CHAT_NO_MESSAGES": "没有可显示的消息",
"CHAT_SEND_MESSAGE": "发送",
"CHAT_TYPE_MESSAGE": "请在此输入消息",
"TITLE_CONTRIBUTORS": "贡献者",
"MESSAGE_CONNECTION_UNTRUSTED": "本连接不是可信的连接",
"MESSAGE_TOGGLE_NAVIGATION": "导航",
"BANDWIDTH_INFO": "带宽信息",
"BANDWIDTH_INFO_INCOMING": "接收:",
"BANDWIDTH_INFO_OUTGOING": "发送:",
"BANDWIDTH_INFO_VIDEO_RES": "视频分辨率:",
"IN_CALL": "通话中:",
"LAST_CALL": "最近通话:",
"OPEN_NEW_WINDOW": "打开新窗口",
"CHANGE_LOGIN_INFO": "修改登录信息",
"LOGOUT": "退出登录",
"ABOUT": "关于",
"HELP": "帮助",
"CONTRIBUTORS": "贡献者",
"TITLE_PREVIEW_SETTINGS": "设置摄像头和麦克风",
"CAMERA_SETTINGS": "摄像头:",
"MIC_SETTINGS": "麦克风",
"SAVE": "保存",
"LOADING": "加载中",
"ERRORS" : "错误",
"CALLING_TO": "正在呼叫 ",
"CANCELLING": "正在取消",
"DETERMINING_SPEED": "检查网速...",
"CALL_HISTORY": "通话历史",
"CLEAR_CALL_HISTORY": "清除历史记录",
"NO_CALL_HISTORY": "尚无任何通话",
"ENTER_EXTENSION": "输入号码",
"CALL_EXTENSION": "呼叫",
"LOGIN": "登录",
"LOGIN_INFORMATION": "登录信息",
"SAVE_LOGIN_INFORMATION": "保存登录信息",
"INVALID_LOGIN_FIELDS": "请检查下面的项目并重试",
"NAME": "姓名",
"YOUR_NAME": "你的姓名",
"EMAIL": "电子邮件",
"YOUR_EMAIL": "你的电子邮件",
"USER": "用户名",
"PASSWORD": "密码",
"CALLER_ID": "主叫号码",
"HOSTNAME": "主机名",
"WEBSOCKET_URL": "Websocket URL",
"SETTINGS": "设置",
"DEVICE_SETTINGS": "设备设置",
"SHARE_DEVICE": "共享设备",
"SPEAKER": "喇叭:",
"SPEAKER_FEATURE": "你的浏览器好像不支持该设置",
"PREVIEW_SETTINGS": "预览设置",
"REFRESH_DEVICE_LIST": "刷新设备列表",
"GENERAL_SETTINGS": "通话设置:",
"USE_VIDEO": "启用视频",
"USE_STEREO_AUDIO": "立体声",
"USE_STUN": "启用STUN",
"SCALE_VIDEO": "自动缩放远端视频到本地摄像头分辨率",
"ASK_BEFORE_RECOVER": "在恢复上一次通话前询问",
"BEST_FRAME_RATE": "最佳帧率:",
"AUDIO_SETTINGS": "音频设置:",
"ECHO_CANCEL": "回声消除",
"NOISE_SUPPRESSION": "噪声抑制",
"HIGHPASS_FILTER": "高通滤波",
"VIDEO_SETTINGS": "视频设置:",
"REMOTE_ENCODER": "已启用专用远端编码器",
"AUTO_SPEED_RES": "根据网速自动选择最佳分辨率",
"RECHECK_BANDWIDTH": "每次通话前重新检查网速",
"CHECK_NETWORK_SPEED": "检查网速",
"VIDEO_QUALITY": "视频质量:",
"MAX_INCOMING_BANDWIDTH": "最大接收带宽:",
"MAX_OUTGOING_BANDWIDTH": "最大发送带宽:",
"FACTORY_RESET": "恢复出厂设置",
"SAVE_DEVICE_SETTINGS": "保存设备设置",
"BROWSER_COMPATIBILITY": "检查浏览器兼容性",
"REFRESH_MEDIA_DEVICES": "检查媒体设备",
"BROWSER_WITHOUT_WEBRTC": "错误浏览器不支持WebRTC",
"CHECK_PERMISSION_MEDIA": "检查媒体使用权限",
"CHECK_PROVISIONING_CONF": "自在自动配置",
"CHECK_LOGIN": "正在检查登录信息",
"CHECK_CONNECTION_SPEED": "检查网速",
"ERROR_PERMISSION_MEDIA": "错误:未授权使用麦克风或摄像头",
"ERROR_PROVISIONING_CONF": "错误:自动配置失败",
"PLEASE_WAIT": "请稍候...",
"CANCEL": "取消",
"CHAT_TITLE_VOL_MINUS": "音量-",
"CHAT_TITLE_VOL_PLUS": "音量+",
"CHAT_TITLE_GAIN_MINUS": "增益-",
"CHAT_TITLE_GAIN_PLUS": "增益+",
"CHAT_VOL_MINUS": "音量-",
"CHAT_VOL_PLUS": "音量+",
"CHAT_GAIN_MINUS": "增益-",
"CHAT_GAIN_PLUS": "增益+"
}

View File

@ -2,19 +2,19 @@
<ul class="nav nav-tabs" role="tablist" ng-init="activePane = 'members'"> <ul class="nav nav-tabs" role="tablist" ng-init="activePane = 'members'">
<li role="presentation" ng-class="{'active': activePane == 'members'}"> <li role="presentation" ng-class="{'active': activePane == 'members'}">
<a ng-click="activePane = 'members'" href=""> <a ng-click="activePane = 'members'" href="">
Members {{ 'CHAT_TITLE_MEMBERS' | translate}}
</a> </a>
</li> </li>
<li role="presentation" ng-class="{'active': activePane == 'chat'}"> <li role="presentation" ng-class="{'active': activePane == 'chat'}">
<a ng-click="activePane = 'chat'" href=""> <a ng-click="activePane = 'chat'" href="">
Chat {{ 'CHAT_TITLE' | translate }}
</a> </a>
</li> </li>
</ul> </ul>
<div class="chat-members" ng-show="activePane == 'members'"> <div class="chat-members" ng-show="activePane == 'members'">
<div ng-show="!members.length"> <div ng-show="!members.length">
<p class="text-center text-muted">There are no members to show.</p> <p class="text-center text-muted">{{ 'CHAT_NO_MEMBERS' | translate }}</p>
</div> </div>
<div ng-repeat="member in members" class="chat-member-item" ng-class="{ opened: $index == openId }"> <div ng-repeat="member in members" class="chat-member-item" ng-class="{ opened: $index == openId }">
@ -56,33 +56,33 @@
<div class="admin-controls" ng-if="verto.data.confRole == 'moderator'" ng-show="$index == $parent.openId"> <div class="admin-controls" ng-if="verto.data.confRole == 'moderator'" ng-show="$index == $parent.openId">
<div> <div>
<div class="col-md-6 ctrl-section"> <div class="col-md-6 ctrl-section">
<h3>General</h3> <h3>{{ 'CHAT_GENERAL' | translate }}</h3>
<div class="group btn-group-justified"> <div class="group btn-group-justified">
<a href="" class="btn btn-xs" ng-click="confKick(member.id)" title="Kick"> <a href="" class="btn btn-xs" ng-click="confKick(member.id)" title="{{ 'CHAT_TITLE_KICK' | translate }}">
<i class="mdi-fw mdi-av-not-interested"></i> <i class="mdi-fw mdi-av-not-interested"></i>
Kick {{ 'CHAT_KICK' | translate }}
</a> </a>
<a href="" class="btn btn-xs" ng-click="confVideoFloor(member.id)" title="Video Floor"> <a href="" class="btn btn-xs" ng-click="confVideoFloor(member.id)" title="{{ 'CHAT_TITLE_VIDEO_FLOOR' | translate }}">
<i class="mdi-fw mdi-action-aspect-ratio"></i> <i class="mdi-fw mdi-action-aspect-ratio"></i>
Floor {{ 'CHAT_FLOOR' | translate }}
</a> </a>
<a href="" class="btn btn-xs" ng-click="confTransfer(member.id)" title="Transfer"> <a href="" class="btn btn-xs" ng-click="confTransfer(member.id)" title="{{ 'CHAT_TITLE_TRANSFER' | translate }}">
<i class="mdi-fw mdi-communication-call-made"></i> <i class="mdi-fw mdi-communication-call-made"></i>
<span style="margin-left: -9px">Transfer</span> <span style="margin-left: -9px">{{ 'CHAT_TRANSFER' | translate }}</span>
</a> </a>
</div> </div>
</div> </div>
<div class="col-md-6 ctrl-section"> <div class="col-md-6 ctrl-section">
<h3>Banner</h3> <h3>{{ 'CHAT_BANNER' | translate }}</h3>
<div class="group btn-group-justified"> <div class="group btn-group-justified">
<a href="" class="btn btn-xs" ng-click="confBanner(member.id)" title="Set"> <a href="" class="btn btn-xs" ng-click="confBanner(member.id)" title="{{ 'CHAT_TITLE_SET' | translate }}">
<i class="mdi-fw mdi-toggle-radio-button-on"></i> <i class="mdi-fw mdi-toggle-radio-button-on"></i>
Set {{ 'CHAT_SET' | translate }}
</a> </a>
<a href="" class="btn btn-xs" ng-click="confResetBanner(member.id)" title="Reset"> <a href="" class="btn btn-xs" ng-click="confResetBanner(member.id)" title="{{ 'CHAT_TITLE_RESET' | translate }}">
<i class="mdi-fw mdi-content-clear"></i> <i class="mdi-fw mdi-content-clear"></i>
Reset {{ 'CHAT_RESET' | translate }}
</a> </a>
</div> </div>
</div> </div>
@ -90,81 +90,81 @@
<div> <div>
<div class="col-md-6 ctrl-section" ng-if="conf.canvasCount > 1"> <div class="col-md-6 ctrl-section" ng-if="conf.canvasCount > 1">
<h3>Canvas</h3> <h3>{{ 'CHAT_CANVAS' | translate }}</h3>
<div class="group btn-group-justified"> <div class="group btn-group-justified">
<a href="" class="btn btn-xs" ng-click="confCanvasIn(member.id, 'prev')" title="Canvas In"> <a href="" class="btn btn-xs" ng-click="confCanvasIn(member.id, 'prev')" title="{{ 'CHAT_CANVAS_IN' | translate }}">
<i class="mdi-fw mdi-action-open-in-browser"></i> <i class="mdi-fw mdi-action-open-in-browser"></i>
Prev {{ 'CHAT_PREV' | translate }}
</a> </a>
<a href="" class="btn btn-xs" ng-click="confCanvasIn(member.id)" title="Canvas In"> <a href="" class="btn btn-xs" ng-click="confCanvasIn(member.id)" title="{{ 'CHAT_CANVAS_IN' | translate }}">
<i class="mdi-fw mdi-action-open-in-browser"></i> <i class="mdi-fw mdi-action-open-in-browser"></i>
Id Id
</a> </a>
<a href="" class="btn btn-xs" ng-click="confCanvasIn(member.id, 'next')" title="Canvas In"> <a href="" class="btn btn-xs" ng-click="confCanvasIn(member.id, 'next')" title="{{ 'CHAT_CANVAS_IN' | translate }}">
<i class="mdi-fw mdi-action-open-in-browser"></i> <i class="mdi-fw mdi-action-open-in-browser"></i>
Next {{ 'CHAT_NEXT' | translate }}
</a> </a>
</div> </div>
<div class="group btn-group-justified"> <div class="group btn-group-justified">
<a href="" class="btn btn-xs" ng-click="confCanvasOut(member.id, 'prev')" title="Canvas Out"> <a href="" class="btn btn-xs" ng-click="confCanvasOut(member.id, 'prev')" title="{{ 'CHAT_CANVAS_OUT' | translate }}">
<i class="mdi-fw mdi-fw mdi-action-system-update-tv"></i> <i class="mdi-fw mdi-fw mdi-action-system-update-tv"></i>
Prev {{ 'CHAT_PREV' | translate }}
</a> </a>
<a href="" class="btn btn-xs" ng-click="confCanvasOut(member.id)" title="Canvas Out"> <a href="" class="btn btn-xs" ng-click="confCanvasOut(member.id)" title="{{ 'CHAT_CANVAS_OUT' | translate }}">
<i class="mdi-fw mdi-fw mdi-action-system-update-tv"></i> <i class="mdi-fw mdi-fw mdi-action-system-update-tv"></i>
Id Id
</a> </a>
<a href="" class="btn btn-xs" ng-click="confCanvasOut(member.id, 'next')" title="Canvas Out"> <a href="" class="btn btn-xs" ng-click="confCanvasOut(member.id, 'next')" title="{{ 'CHAT_CANVAS_OUT' | translate }}">
<i class="mdi-fw mdi-fw mdi-action-system-update-tv"></i> <i class="mdi-fw mdi-fw mdi-action-system-update-tv"></i>
Next {{ 'CHAT_NEXT' | translate }}
</a> </a>
</div> </div>
<div class="group btn-group-justified"> <div class="group btn-group-justified">
<a href="" class="btn btn-xs" ng-click="confLayer(member.id, 'prev')" title="Layer"> <a href="" class="btn btn-xs" ng-click="confLayer(member.id, 'prev')" title="{{ 'CHAT_LAYER' | translate }}">
<i class="mdi-fw mdi-action-view-carousel"></i> <i class="mdi-fw mdi-action-view-carousel"></i>
Prev {{ 'CHAT_PREV' | translate }}
</a> </a>
<a href="" class="btn btn-xs" ng-click="confLayer(member.id)" title="Layer"> <a href="" class="btn btn-xs" ng-click="confLayer(member.id)" title="{{ 'CHAT_LAYER' | translate }}">
<i class="mdi-fw mdi-action-view-carousel"></i> <i class="mdi-fw mdi-action-view-carousel"></i>
Id Id
</a> </a>
<a href="" class="btn btn-xs" ng-click="confLayer(member.id, 'next')" title="Layer"> <a href="" class="btn btn-xs" ng-click="confLayer(member.id, 'next')" title="{{ 'CHAT_LAYER' | translate }}">
<i class="mdi-fw mdi-action-view-carousel"></i> <i class="mdi-fw mdi-action-view-carousel"></i>
Next {{ 'CHAT_NEXT' | translate }}
</a> </a>
</div> </div>
</div> </div>
<div class="col-md-6 ctrl-section"> <div class="col-md-6 ctrl-section">
<h3>Audio/Video</h3> <h3>{{ 'CHAT_AUDIO_VIDEO' | translate }}</h3>
<div class="group btn-group-justified"> <div class="group btn-group-justified">
<a href="" class="btn btn-xs" ng-click="confMuteMic(member.id)" title="Mute/Unmute Mic"> <a href="" class="btn btn-xs" ng-click="confMuteMic(member.id)" title="{{ 'CHAT_TITLE_MUTE_UNMUTE_MIC' | translate }}">
<i class="mdi-fw" ng-class="member.status.audio.muted ? 'mdi-av-mic-off' : 'mdi-av-mic'"></i> <i class="mdi-fw" ng-class="member.status.audio.muted ? 'mdi-av-mic-off' : 'mdi-av-mic'"></i>
{{ member.status.audio.muted ? 'Unmute' : 'Mute' }} {{ member.status.audio.muted ? 'CHAT_UNMUTE_MIC' : 'CHAT_MUTE_MIC' | translate }}
</a> </a>
<a href="" ng-class="{ 'disabled': !member.status.video }" class="btn btn-xs" ng-click="confMuteVideo(member.id)" title="Mute/Unmute Video"> <a href="" ng-class="{ 'disabled': !member.status.video }" class="btn btn-xs" ng-click="confMuteVideo(member.id)" title="{{ 'CHAT_TITLE_MUTE_UNMUTE_VIDEO' | translate }}">
<i class="mdi-fw" ng-class="member.status.video.muted ? 'mdi-av-videocam-off' : 'mdi-av-videocam'"></i> <i class="mdi-fw" ng-class="member.status.video.muted ? 'mdi-av-videocam-off' : 'mdi-av-videocam'"></i>
{{ member.status.video.muted ? 'Unmute' : 'Mute' }} {{ member.status.video.muted ? 'CHAT_UNMUTE_MIC' : 'CHAT_MUTE_MIC' | translate }}
</a> </a>
</div> </div>
<div class="group btn-group-justified"> <div class="group btn-group-justified">
<a href="" class="btn btn-xs" ng-click="confVolumeDown(member.id)" title="Vol "> <a href="" class="btn btn-xs" ng-click="confVolumeDown(member.id)" title="{{ 'CHAT_TITLE_VOL_MINUS' | translate }}">
<i class="mdi-fw mdi-av-volume-down"></i> <i class="mdi-fw mdi-av-volume-down"></i>
Vol {{ 'CHAT_VOL_MINUS' | translate }}
</a> </a>
<a href="" class="btn btn-xs" ng-click="confVolumeUp(member.id)" title="Vol +"> <a href="" class="btn btn-xs" ng-click="confVolumeUp(member.id)" title="{{ 'CHAT_TITLE_VOL_MINUS' | translate }}">
<i class="mdi-fw mdi-av-volume-up"></i> <i class="mdi-fw mdi-av-volume-up"></i>
Vol + {{ 'CHAT_VOL_PLUS' | translate }}
</a> </a>
</div> </div>
<div class="group btn-group-justified"> <div class="group btn-group-justified">
<a href="" class="btn btn-xs" ng-click="confGainDown(member.id)" title="Gain "> <a href="" class="btn btn-xs" ng-click="confGainDown(member.id)" title="{{ 'CHAT_TITLE_VOL_MINUS' | translate }}">
<i class="mdi-fw mdi-av-volume-down"></i> <i class="mdi-fw mdi-av-volume-down"></i>
Gain {{ 'CHAT_GAIN_MINUS' | translate }}
</a> </a>
<a href="" class="btn btn-xs" ng-click="confGainUp(member.id)" title="Gain +"> <a href="" class="btn btn-xs" ng-click="confGainUp(member.id)" title="{{ 'CHAT_TITLE_VOL_MINUS' | translate }}">
<i class="mdi-fw mdi-av-volume-up"></i> <i class="mdi-fw mdi-av-volume-up"></i>
Gain + {{ 'CHAT_GAIN_PLUS' | translate }}
</a> </a>
</div> </div>
</div> </div>
@ -176,7 +176,7 @@
<div class="chat-history" ng-show="activePane == 'chat'"> <div class="chat-history" ng-show="activePane == 'chat'">
<div class="chat-messages"> <div class="chat-messages">
<div class="chat-message" ng-show="!messages.length"> <div class="chat-message" ng-show="!messages.length">
<p class="text-center text-muted">There are no messages to show.</p> <p class="text-center text-muted">{{ 'CHAT_NO_MESSAGES' | translate }}</p>
</div> </div>
<div class="chat-message" ng-repeat="message in messages" title="Sent at {{ message.created_at|date }}."> <div class="chat-message" ng-repeat="message in messages" title="Sent at {{ message.created_at|date }}.">
<div class="chat-message-metadata">{{ message.from }}:</div> <div class="chat-message-metadata">{{ message.from }}:</div>
@ -188,9 +188,9 @@
<div class="chat-message-input"> <div class="chat-message-input">
<form ng-submit="send()" > <form ng-submit="send()" >
<div class="chat-message-input-group"> <div class="chat-message-input-group">
<textarea ng-model="message" ng-keydown="($event.keyCode == 13 && $event.shiftKey !== true) && send($event)" required="required" class="form-control input-sm" placeholder="Type your message here..."></textarea> <textarea ng-model="message" ng-keydown="($event.keyCode == 13 && $event.shiftKey !== true) && send($event)" required="required" class="form-control input-sm" placeholder="{{ 'CHAT_TYPE_MESSAGE' | translate }}"></textarea>
<button class="btn btn-success btn-sm" type="submit"> <button class="btn btn-success btn-sm" type="submit">
Send {{ 'CHAT_SEND_MESSAGE' | translate }}
<span class="mdi-navigation-arrow-forward chat-message-input-group-icon-button"></span> <span class="mdi-navigation-arrow-forward chat-message-input-group-icon-button"></span>
</button> </button>
</div> </div>

View File

@ -1,5 +1,5 @@
<div class="modal-header"> <div class="modal-header">
<h3 class="modal-title">Contributors</h3> <h3 class="modal-title">{{ 'TITLE_CONTRIBUTORS' | translate}}</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<ul class="contributors"> <ul class="contributors">

View File

@ -1,6 +1,15 @@
<div ng-show="loading"> <div ng-show="loading">
<h3 style="margin-top: 4%;" class="text-center">Calling to {{ dialpadNumber }}...</h3> <h2 ng-show="cancelled" style="margin-top: 4%;" class="text-center">{{ 'CANCELLING' | translate}}</h2>
<svg class="spinner" width="65px" height="65px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg"> <span ng-show="!cancelled">
<h2 style="margin-top: 4%;" class="text-center">{{ 'DETERMINING_SPEED' | translate}}</h2>
<h4 style="margin-top: 4%;" class="text-center">
{{ 'CALLING_TO' | translate}} {{ dialpadNumber }}...
<a class="btn btn-sm btn-raised btn-warning" ng-click="cancel()">
Cancel<div class="ripple-container"></div>
</a>
</h4>
</span>
<svg class="spinner" width="35px" height="35px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<circle class="path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle> <circle class="path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>
</svg> </svg>
</div> </div>
@ -10,7 +19,7 @@
<div class="panel-heading"> <div class="panel-heading">
<div class="panel-title"> <div class="panel-title">
<i class="mdi-navigation-arrow-back back-icon" ng-click="viewCallsList()" ng-if="call_list"></i> <i class="mdi-navigation-arrow-back back-icon" ng-click="viewCallsList()" ng-if="call_list"></i>
<span ng-if="!call_list">Call History</span> <span ng-if="!call_list">{{ 'CALL_HISTORY' | translate}}</span>
<span ng-if="call_list">{{ call_list[0].number }}</span> <span ng-if="call_list">{{ call_list[0].number }}</span>
<span class="pull-right pull-right-margin dropdown"> <span class="pull-right pull-right-margin dropdown">
@ -18,14 +27,14 @@
<i class="mdi-navigation-more-vert"></i> <i class="mdi-navigation-more-vert"></i>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="" ng-click="clearCallHistory()">Clear History</a></li> <li><a href="" ng-click="clearCallHistory()">{{ 'CLEAR_CALL_HISTORY' | translate}}</a></li>
</ul> </ul>
</span> </span>
</div> </div>
</div> </div>
<ul class="call-history"> <ul class="call-history">
<div ng-if="!has_history"> <div ng-if="!has_history">
<p class="text-center text-muted">No history calls.</p> <p class="text-center text-muted">{{ 'NO_CALL_HISTORY' | translate}}</p>
</div> </div>
<li ng-repeat="number in history_control track by number" ng-if="!call_list"> <li ng-repeat="number in history_control track by number" ng-if="!call_list">
@ -63,7 +72,7 @@
<i class="mdi-action-settings-phone"></i> <i class="mdi-action-settings-phone"></i>
</a> </a>
</span> </span>
<input name="dialpadnumber" type="text" class="form-control text-center" placeholder="Enter an extension" ng-model="dialpadNumber" autofocus/> <input name="dialpadnumber" type="text" class="form-control text-center" placeholder="{{ 'ENTER_EXTENSION' | translate}}" ng-model="dialpadNumber" autofocus/>
<span class="input-group-btn"> <span class="input-group-btn">
<a href="" ng-click="backspace()"> <a href="" ng-click="backspace()">
<i class="mdi-content-backspace"></i> <i class="mdi-content-backspace"></i>
@ -154,7 +163,7 @@
</div> </div>
</div> </div>
<div class="form-group text-center"> <div class="form-group text-center">
<button type="submit" class="btn btn-success btn-fab" ng-click="call(dialpadNumber)" title="Call Extension"> <button type="submit" class="btn btn-success btn-fab" ng-click="call(dialpadNumber)" title="{{ 'CALL_EXTENSION' | translate}}">
<i class="mdi-communication-call"></i> <i class="mdi-communication-call"></i>
</button> </button>
</div> </div>

View File

@ -2,59 +2,59 @@
<div class="col-md-4 col-xs-12 col-sm-12 centered-block"> <div class="col-md-4 col-xs-12 col-sm-12 centered-block">
<div class="panel panel-default shadow-z-2"> <div class="panel panel-default shadow-z-2">
<div class="panel-body"> <div class="panel-body">
<h3>Login</h3> <h3>{{ 'LOGIN' | translate}}</h3>
<div ng-show="form.$submitted && form.$invalid" class="alert alert-danger"> <div ng-show="form.$submitted && form.$invalid" class="alert alert-danger">
<p>Verify the fields bellow and try again.</p> <p>{{ 'INVALID_LOGIN_FIELDS' | translate }}</p>
</div> </div>
<form name="form" class="css-form" novalidate ng-init="advanced = false"> <form name="form" class="css-form" novalidate ng-init="advanced = false">
<div class="form-group {{ (((!form.name.$pristine || form.$submitted) && !form.name.$valid) ? 'has-error': '') }}"> <div class="form-group {{ (((!form.name.$pristine || form.$submitted) && !form.name.$valid) ? 'has-error': '') }}">
<label class="control-label" for="login-name">Name</label> <label class="control-label" for="login-name">{{ 'NAME' | translate}}</label>
<input type="text" name="name" class="form-control" id="login-name" placeholder="Your name" required="" ng-model="verto.data.name" autofocus> <input type="text" name="name" class="form-control" id="login-name" placeholder="{{ 'YOUR_NAME' | translate}}" required="" ng-model="verto.data.name" autofocus>
</div> </div>
<div class="form-group {{ (((!form.email.$pristine || form.$submitted) && !form.email.$valid) ? 'has-error': '') }}"> <div class="form-group {{ (((!form.email.$pristine || form.$submitted) && !form.email.$valid) ? 'has-error': '') }}">
<label class="control-label" for="login-email">Email</label> <label class="control-label" for="login-email">{{ 'EMAIL' | translate }}</label>
<input type="email" name="email" class="form-control" id="login-email" placeholder="Your email" required="" ng-model="verto.data.email" ng-model-options="{debounce: 250}"> <input type="email" name="email" class="form-control" id="login-email" placeholder="{{ 'YOUR_EMAIL' | translate}}" required="" ng-model="verto.data.email" ng-model-options="{debounce: 250}">
</div> </div>
<div class="form-group" ng-hide="!advanced"> <div class="form-group" ng-hide="!advanced">
<label class="control-label" for="login-user">User</label> <label class="control-label" for="login-user">{{ 'USER' | translate}}</label>
<input type="text" class="form-control" id="login-user" placeholder="User" ng-model="verto.data.login"> <input type="text" class="form-control" id="login-user" placeholder="{{ 'USER' | translate}}" ng-model="verto.data.login">
</div> </div>
<div class="form-group" ng-hide="!advanced"> <div class="form-group" ng-hide="!advanced">
<label class="control-label" for="login-password">Password</label> <label class="control-label" for="login-password">{{ 'PASSWORD' | translate}}</label>
<input type="password" class="form-control" id="login-password" placeholder="Password" ng-model="verto.data.password"> <input type="password" class="form-control" id="login-password" placeholder={{ 'PASSWORD' | translate}} ng-model="verto.data.password">
</div> </div>
<div class="form-group" ng-hide="!advanced"> <div class="form-group" ng-hide="!advanced">
<label class="control-label" for="login-callerid">Caller ID</label> <label class="control-label" for="login-callerid">{{ 'CALLER_ID' | translate}}</label>
<input type="text" class="form-control" id="login-callerid" placeholder="Caller ID" ng-model="verto.data.callerid"> <input type="text" class="form-control" id="login-callerid" placeholder="{{ 'CALLER_ID' | translate}}" ng-model="verto.data.callerid">
</div> </div>
<div class="form-group" ng-hide="!advanced"> <div class="form-group" ng-hide="!advanced">
<label class="control-label" for="login-hostname">Hostname</label> <label class="control-label" for="login-hostname">{{ 'HOSTNAME' | translate}}</label>
<input type="text" class="form-control" id="login-hostname" placeholder="Hostname" ng-model="verto.data.hostname"> <input type="text" class="form-control" id="login-hostname" placeholder="{{ 'HOSTNAME' | translate}}" ng-model="verto.data.hostname">
</div> </div>
<div class="form-group" ng-hide="!advanced"> <div class="form-group" ng-hide="!advanced">
<label class="control-label" for="login-wsurl">Websocket URL</label> <label class="control-label" for="login-wsurl">{{ 'WEBSOCKET_URL' | translate}}</label>
<input type="text" class="form-control" id="login-wsurl" placeholder="Websocket URL" ng-model="verto.data.wsURL"> <input type="text" class="form-control" id="login-wsurl" placeholder="{{ 'WEBSOCKET_URL' | translate}}" ng-model="verto.data.wsURL">
</div> </div>
<div class="form-group hide"> <div class="form-group hide">
<label for="login-login">Login</label> <label for="login-login">{{ 'LOGIN' | translate}}</label>
<input type="text" class="form-control" id="login-login" placeholder="Login" ng-model="verto.data.login"> <input type="text" class="form-control" id="login-login" placeholder="{{ 'LOGIN' | translate}}" ng-model="verto.data.login">
</div> </div>
<div class="form-group hide"> <div class="form-group hide">
<label for="login-password">Password</label> <label for="login-password">Password</label>
<input type="text" class="form-control" id="login-password" placeholder="Password" ng-model="verto.data.password"> <input type="text" class="form-control" id="login-password" placeholder="{{ 'PASSWORD' | translate}}" ng-model="verto.data.password">
</div> </div>
<div class="form-group text-right"> <div class="form-group text-right">
<div><a style="margin-top: 13px;" href="" ng-click="advanced = !advanced" class="pull-left">Settings</a></div> <div><a style="margin-top: 13px;" href="" ng-click="advanced = !advanced" class="pull-left">{{ 'SETTINGS' | translate}}</a></div>
<div><button type="submit" class="btn btn-success" ng-click="(form.$valid && login())">Login</button></div> <div><button type="submit" class="btn btn-success" ng-click="(form.$valid && login())">{{ 'LOGIN' | translate}}</button></div>
<div ng-if="googlelogin" class="googlelogin"> <div ng-if="googlelogin" class="googlelogin">
<google-plus-signin clientid="{{src/partials/login.html}}" class="center"> <google-plus-signin clientid="{{src/partials/login.html}}" class="center">
</google-plus-signin> </google-plus-signin>

View File

@ -2,13 +2,13 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="https-untrusted navbar-center" ng-show="!safeProtocol"> <div class="https-untrusted navbar-center" ng-show="!safeProtocol">
<div class="https-message"> <div class="https-message">
This Connection is Untrusted. {{ 'MESSAGE_CONNECTION_UNTRUSTED' | translate }}
</div> </div>
</div> </div>
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span> <span class="sr-only">{{ 'MESSAGE_TOGGLE_NAVIGATION' | translate }}</span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
@ -25,10 +25,10 @@
<span class="caret"></span> <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu drop-net-info" role="menu" ng-click="$event.stopPropagation()"> <ul class="dropdown-menu drop-net-info" role="menu" ng-click="$event.stopPropagation()">
<li><a class="title">Bandwidth Info</a></li> <li><a class="title">{{ 'BANDWIDTH_INFO' | translate }}</a></li>
<li><a>Outgoing: {{bandUp}} Kbps</a></li> <li><a>{{ 'BANDWIDTH_INFO_OUTGOING' | translate }} {{bandUp}} Kbps</a></li>
<li><a>Incoming: {{bandDown}} Kbps</a></li> <li><a>{{ 'BANDWIDTH_INFO_INCOMING' | translate }} {{bandDown}} Kbps</a></li>
<li><a>Video Resolution: {{vidRes}}</a></li> <li><a>{{ 'BANDWIDTH_INFO_VIDEO_RES' | translate }} {{vidRes}}</a></li>
</ul> </ul>
</li> </li>
<li> <li>
@ -38,11 +38,11 @@
</li> </li>
<li> <li>
<a href="" class="incall-number" ng-show="storage.data.called_number"> <a href="" class="incall-number" ng-show="storage.data.called_number">
{{ storage.data.called_number && storage.data.userStatus == 'connecting' ? 'Last Call: ' : 'In Call: ' }} {{ storage.data.called_number }} {{ storage.data.called_number && storage.data.userStatus != 'connected' ? 'LAST_CALL' : 'IN_CALL' | translate }} {{ storage.data.called_number }}
</a> </a>
</li> </li>
<li class="navbar-item-icon" ng-show="verto.data.connected"> <li class="navbar-item-icon" ng-show="verto.data.connected">
<a href="" ng-click="openModal('partials/modal_settings.html', 'ModalSettingsController')"> <a href="" ng-click="toggleSettings()">
<i class="mdi-action-settings"></i> <i class="mdi-action-settings"></i>
</a> </a>
</li> </li>
@ -63,10 +63,10 @@
<span class="caret"></span> <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li><a href="#/?sessid=random" target="_blank">Open New Window</a></li> <li><a href="#/?sessid=random" target="_blank">{{ 'OPEN_NEW_WINDOW' | translate}}</a></li>
<li><a href="" ng-click="openModal('partials/modal_logininfo.html', 'ModalLoginInformationController')">Change Login Information</a></li> <li><a href="" ng-click="openModal('partials/modal_logininfo.html', 'ModalLoginInformationController')">{{ 'CHANGE_LOGIN_INFO' | translate}}</a></li>
<!--<li><a href="#">View Device Settings</a></li>--> <!--<li><a href="#">View Device Settings</a></li>-->
<li><a href="" ng-click="logout()">Logout</a></li> <li><a href="" ng-click="logout()">{{ 'LOGOUT' | translate}}</a></li>
</ul> </ul>
</li> </li>
<li class="navbar-item-icon"> <li class="navbar-item-icon">
@ -75,9 +75,9 @@
<span class="caret"></span> <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li><a href="" ng-click="showAbout()">About</a></li> <li><a href="" ng-click="showAbout()">{{ 'ABOUT' | translate}}</a></li>
<li><a href="" ng-click="showContributors()">Contributors</a></li> <li><a href="" ng-click="showContributors()">{{ 'CONTRIBUTORS' | translate}}</a></li>
<li><a href="https://freeswitch.org/confluence/x/MQCT" target="_blank">Help</a></li> <li><a href="https://freeswitch.org/confluence/x/MQCT" target="_blank">{{ 'HELP' | translate}}</a></li>
</ul> </ul>
</li> </li>

View File

@ -1,35 +1,35 @@
<div class="modal-header"> <div class="modal-header">
<h3 class="modal-title">Login Information</h3> <h3 class="modal-title">{{ 'LOGIN_INFORMATION' | translate}}</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
<label for="name">Name:</label> <label for="name">{{ 'NAME' | translate}}</label>
<input type="text" name="name" class="form-control" ng-model="storage.data.name" ng-value="storage.data.name"/> <input type="text" name="name" class="form-control" ng-model="storage.data.name" ng-value="storage.data.name"/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="email">Email:</label> <label for="email">{{ 'EMAIL' | translate}}</label>
<input type="text" name="email" class="form-control" ng-model="storage.data.email" ng-value="storage.data.email"/> <input type="text" name="email" class="form-control" ng-model="storage.data.email" ng-value="storage.data.email"/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="user">User</label> <label class="control-label" for="user">{{ 'USER' | translate}}</label>
<input type="text" class="form-control" id="user" placeholder="User" ng-model="storage.data.login"> <input type="text" class="form-control" id="user" placeholder="User" ng-model="storage.data.login">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="password">Password</label> <label class="control-label" for="password">{{ 'PASSWORD' | translate}}</label>
<input type="password" class="form-control" id="password" placeholder="Password" ng-model="verto.data.password"> <input type="password" class="form-control" id="password" placeholder="Password" ng-model="verto.data.password">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="callerid">Caller ID</label> <label class="control-label" for="callerid">{{ 'CALLER_ID' | translate}}</label>
<input type="text" class="form-control" id="callerid" placeholder="Caller ID" ng-model="verto.data.callerid"> <input type="text" class="form-control" id="callerid" placeholder="Caller ID" ng-model="verto.data.callerid">
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<!-- <button class="btn btn-primary" ng-click="cancel()">Cancel</button> --> <!-- <button class="btn btn-primary" ng-click="cancel()">Cancel</button> -->
<button class="btn btn-primary" ng-click="ok()">Save Login Information</button> <button class="btn btn-primary" ng-click="ok()">{{ 'SAVE_LOGIN_INFORMATION' | translate}}</button>
</div> </div>

View File

@ -1,159 +0,0 @@
<div class="modal-header">
<h3 class="modal-title">Device Settings</h3>
</div>
<div class="modal-body">
<div class="form-group" ng-show="mydata.useVideo">
<label for="settings-camera">Camera:</label>
<select name="camera" id="settings-camera" class="form-control"
ng-model="mydata.selectedVideo" ng-options="item.id as item.label for item in verto.data.videoDevices">
</select>
</div>
<div class="form-group" ng-show="mydata.useVideo">
<label for="settings-share-device">Share device:</label>
<select name="share-device" id="settings-share-device" class="form-control"
ng-model="mydata.selectedShare" ng-options="item.id as item.label for item in verto.data.shareDevices">
</select>
</div>
<div class="form-group">
<label for="settings-microphone">Microphone:</label>
<select name="microphone" id="settings-microphone" class="form-control"
ng-model="mydata.selectedAudio" ng-options="item.id as item.label for item in verto.data.audioDevices">
</select>
</div>
<div class="form-group">
<label for="settings-microphone">
Speaker:
<span ng-show="!speakerFeature" class="unsupported">
Your browser doesn't seem to support this feature
</span>
</label>
<select name="microphone" id="settings-microphone" class="form-control" ng-disabled="!speakerFeature"
ng-model="mydata.selectedSpeaker" ng-options="item.id as item.label for item in verto.data.speakerDevices">
</select>
</div>
<a class="btn btn-primary" href="#/preview" ng-click="ok()">Preview Settings</a>
<a class="btn btn-primary" href="" ng-click="refreshDeviceList()">Refresh device list</a>
<div class="form-group">
<label for="settings-microphone">General settings:</label>
<div class="checkbox">
<label>
<input type="checkbox" name="use_video" value="mydata.useVideo" ng-model="mydata.useVideo">
Use Video
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="use_stereo_audio" ng-value="mydata.useStereo" ng-model="mydata.useStereo">
Stereo Audio
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="use_stun" ng-value="mydata.useSTUN" ng-model="mydata.useSTUN">
Use STUN
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="mirror_input" ng-value="mydata.mirrorInput" ng-model="mydata.mirrorInput">
Scale Remote Video To Match Camera Resolution
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="ask_recover_call" ng-value="mydata.askRecoverCall" ng-model="mydata.askRecoverCall">
Ask before recovering call
</label>
</div>
</div>
<div class="form-group">
<label for="settings-framerate">Best frame rate:</label>
<select name="settings-framerate" id="settings-framerate" class="form-control"
ng-model="mydata.bestFrameRate"
ng-options="item.id as item.label for item in verto.framerate"></select>
</div>
<div class="form-group">
<label for="settings-microphone">Audio settings:</label>
<div class="checkbox">
<label>
<input type="checkbox" name="googEchoCancellation" value="mydata.googEchoCancellation" ng-model="mydata.googEchoCancellation">
Echo Cancellation
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="googNoiseSuppression" value="mydata.googNoiseSuppression" ng-model="mydata.googNoiseSuppression">
Noise Suppression
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="googHighpassFilter" value="mydata.googHighpassFilter" ng-model="mydata.googHighpassFilter">
Highpass Filter
</label>
</div>
</div>
<div class="form-group">
<label>Video settings:</label> <br>
<input type="hidden" name="use_dedenc" ng-value="mydata.useDedenc" ng-model="mydata.useDedenc">
<div ng-show="mydata.useDedenc" class="dedicated_encoder">
<p>Dedicated Remote Encoder enabled.</b>
</div>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="mydata.autoBand" ng-change="checkAutoBand(mydata.autoBand)">
Automatically determine speed and resolution settings
</label>
</div>
<div class="checkbox" ng-show="mydata.autoBand">
<label>
<input type="checkbox" ng-model="mydata.testSpeedJoin">
Recheck bandwidth before each outgoing call
</label>
</div>
<a ng-show="mydata.autoBand" class="btn btn-primary" href="" ng-click="testSpeed()">Check Network Speed</a> <span ng-bind="speedMsg"></span>
<div ng-show="!mydata.autoBand">
<label for="video-quality">Video quality:</label>
<select name="video_quality" id="video-quality" class="form-control"
ng-disabled="mydata.autoBand"
ng-model="mydata.vidQual"
ng-change="checkVideoQuality(mydata.vidQual)"
ng-options="item.id as item.label for item in verto.videoQuality"></select>
</div>
<div ng-show="!mydata.autoBand">
<label for="incoming-bandwidth">Max incoming bandwidth:</label>
<select name="incoming_bandwidth" id="incoming-bandwidth" class="form-control"
ng-model="mydata.incomingBandwidth"
ng-change="checkUseDedRemoteEncoder(mydata.incomingBandwidth)"
ng-options="item.id as item.label for item in verto.bandwidth"></select>
</div>
<div ng-show="!mydata.autoBand">
<label for="outgoing-bandwidth">Max outgoing bandwidth:</label>
<select name="outgoing_bandwidth" id="outgoing-bandwidth" class="form-control"
ng-model="mydata.outgoingBandwidth"
ng-options="item.id as item.label for item in verto.bandwidth"></select>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-danger pull-left btn-pull-left" ng-click="resetSettings()">Factory reset</button>
<!-- <button class="btn btn-primary" ng-click="cancel()">Cancel</button> -->
<button class="btn btn-primary" ng-click="ok()">Save Device Settings</button>
</div>

View File

@ -2,7 +2,7 @@
<div class="col-md-4 col-sm-12 col-xs-12 centered-block"> <div class="col-md-4 col-sm-12 col-xs-12 centered-block">
<div class="panel panel-material-blue-900 shadow-z-2 "> <div class="panel panel-material-blue-900 shadow-z-2 ">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title text-center">Setup your camera and microphone settings</h3> <h3 class="panel-title text-center">{{ 'TITLE_PREVIEW_SETTINGS' | translate }}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div class="preview-wrapper"> <div class="preview-wrapper">
@ -20,13 +20,13 @@
</div> </div>
<form name="form"> <form name="form">
<div class="form-group col-md-5 col-sm-12 col-xs-12" ng-show="true"> <div class="form-group col-md-5 col-sm-12 col-xs-12" ng-show="true">
<label for="settings-camera">Camera:</label> <label for="settings-camera">{{ 'CAMERA_SETTINGS' | translate }}</label>
<select name="camera" id="settings-camera" class="form-control" ng-model="storage.data.selectedVideo" <select name="camera" id="settings-camera" class="form-control" ng-model="storage.data.selectedVideo"
ng-options="item.id as item.label for item in verto.data.videoDevices" ng-change="localVideo()" > ng-options="item.id as item.label for item in verto.data.videoDevices" ng-change="localVideo()" >
</select> </select>
</div> </div>
<div class="form-group col-md-5 col-sm-12 col-xs-12" ng-show="true"> <div class="form-group col-md-5 col-sm-12 col-xs-12" ng-show="true">
<label for="settings-microphone">Microphone:</label> <label for="settings-microphone">{{ 'MIC_SETTINGS' | translate }}</label>
<select name="microphone" id="settings-microphone" class="form-control" ng-model="storage.data.selectedAudio" <select name="microphone" id="settings-microphone" class="form-control" ng-model="storage.data.selectedAudio"
ng-options="item.id as item.label for item in verto.data.audioDevices" ng-change="localVideo()"> ng-options="item.id as item.label for item in verto.data.audioDevices" ng-change="localVideo()">
</select> </select>
@ -36,8 +36,8 @@
</a> </a>
<div class="form-group text-center"> <div class="form-group text-center">
<button type="submit" class="btn btn-success" ng-click="endPreview()" title="Save"> <button type="submit" class="btn btn-success" ng-click="endPreview()" title="{{ 'SAVE' | translate }}">
Save {{ 'SAVE' | translate }}
</button> </button>
</div> </div>
</form> </form>

View File

@ -0,0 +1,159 @@
<div id="settings" ng-controller="SettingsController">
<div class="content">
<div class="col-md-4">
<div class="form-group" ng-show="mydata.useVideo">
<label for="settings-camera">{{ 'CAMERA_SETTINGS' | translate }}</label>
<select name="camera" id="settings-camera" class="form-control"
ng-model="mydata.selectedVideo" ng-options="item.id as item.label for item in verto.data.videoDevices">
</select>
</div>
<div class="form-group" ng-show="mydata.useVideo">
<label for="settings-share-device">{{ 'SHARE_DEVICE' | translate }}</label>
<select name="share-device" id="settings-share-device" class="form-control"
ng-model="mydata.selectedShare" ng-options="item.id as item.label for item in verto.data.shareDevices">
</select>
</div>
<div class="form-group">
<label for="settings-microphone">{{ 'MIC_SETTINGS' | translate}}</label>
<select name="microphone" id="settings-microphone" class="form-control"
ng-model="mydata.selectedAudio" ng-options="item.id as item.label for item in verto.data.audioDevices">
</select>
</div>
<div class="form-group">
<label for="settings-microphone">
{{ 'SPEAKER' | translate }}
<span ng-show="!speakerFeature" class="unsupported">
{{ 'SPEAKER_FEATURE' | translate }}
</span>
</label>
<select name="microphone" id="settings-microphone" class="form-control" ng-disabled="!speakerFeature"
ng-model="mydata.selectedSpeaker" ng-options="item.id as item.label for item in verto.data.speakerDevices">
</select>
</div>
<div class="form-group">
<label for="settings-framerate">{{ 'BEST_FRAME_RATE' | translate }}</label>
<select name="settings-framerate" id="settings-framerate" class="form-control"
ng-model="mydata.bestFrameRate"
ng-options="item.id as item.label for item in verto.framerate"></select>
</div>
<a class="btn btn-primary" href="" ng-click="showPreview()">{{ 'PREVIEW_SETTINGS' | translate }}</a>
<a class="btn btn-primary" href="" ng-click="refreshDeviceList()">{{ 'REFRESH_DEVICE_LIST' | translate }}</a>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="settings-microphone">{{ 'GENERAL_SETTINGS' | translate }}</label>
<div class="checkbox">
<label>
<input type="checkbox" name="use_video" value="mydata.useVideo" ng-model="mydata.useVideo">
<span ng-bind="'USE_VIDEO' | translate"></span>
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="use_stereo_audio" ng-value="mydata.useStereo" ng-model="mydata.useStereo">
<span ng-bind="'USE_STEREO_AUDIO' | translate"></span>
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="use_stun" ng-value="mydata.useSTUN" ng-model="mydata.useSTUN">
<span ng-bind="'USE_STUN' | translate"></span>
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="mirror_input" ng-value="mydata.mirrorInput" ng-model="mydata.mirrorInput">
<span ng-bind="'SCALE_VIDEO' | translate"></span>
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="ask_recover_call" ng-value="mydata.askRecoverCall" ng-model="mydata.askRecoverCall">
<span ng-bind="'ASK_BEFORE_RECOVER' | translate"></span>
</label>
</div>
<div class="form-group">
<label for="settings-microphone">{{ 'AUDIO_SETTINGS' | translate }}</label>
<div class="checkbox">
<label>
<input type="checkbox" name="googEchoCancellation" value="mydata.googEchoCancellation" ng-model="mydata.googEchoCancellation">
<span ng-bind="'ECHO_CANCEL' | translate"></span>
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="googNoiseSuppression" value="mydata.googNoiseSuppression" ng-model="mydata.googNoiseSuppression">
<span ng-bind="'NOISE_SUPPRESSION' | translate"></span>
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="googHighpassFilter" value="mydata.googHighpassFilter" ng-model="mydata.googHighpassFilter">
<span ng-bind="'HIGHPASS_FILTER' | translate"></span>
</label>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>{{ 'VIDEO_SETTINGS' | translate }}</label> <br>
<input type="hidden" name="use_dedenc" ng-value="mydata.useDedenc" ng-model="mydata.useDedenc">
<div ng-show="mydata.useDedenc" class="dedicated_encoder">
<p>{{ 'REMOTE_ENCODER' | translate }}</b>
</div>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="mydata.autoBand" ng-change="checkAutoBand(mydata.autoBand)">
<span ng-bind="'AUTO_SPEED_RES' | translate"></span>
</label>
</div>
<div class="checkbox" ng-show="mydata.autoBand">
<label>
<input type="checkbox" ng-model="mydata.testSpeedJoin">
<span ng-bind="'RECHECK_BANDWIDTH' | translate"></span>
</label>
</div>
<a ng-show="mydata.autoBand" class="btn btn-primary" href="" ng-click="testSpeed()">{{ 'CHECK_NETWORK_SPEED' | translate }}</a> <span ng-bind="speedMsg"></span>
<div ng-show="!mydata.autoBand">
<label for="video-quality">{{ 'VIDEO_QUALITY' | translate }}</label>
<select name="video_quality" id="video-quality" class="form-control"
ng-disabled="mydata.autoBand"
ng-model="mydata.vidQual"
ng-change="checkVideoQuality(mydata.vidQual)"
ng-options="item.id as item.label for item in verto.videoQuality"></select>
</div>
<div ng-show="!mydata.autoBand">
<label for="incoming-bandwidth">{{ 'MAX_INCOMING_BANDWIDTH' | translate }}</label>
<select name="incoming_bandwidth" id="incoming-bandwidth" class="form-control"
ng-model="mydata.incomingBandwidth"
ng-change="checkUseDedRemoteEncoder(mydata.incomingBandwidth)"
ng-options="item.id as item.label for item in verto.bandwidth"></select>
</div>
<div ng-show="!mydata.autoBand">
<label for="outgoing-bandwidth">{{ 'MAX_OUTGOING_BANDWIDTH' | translate }}</label>
<select name="outgoing_bandwidth" id="outgoing-bandwidth" class="form-control"
ng-model="mydata.outgoingBandwidth"
ng-options="item.id as item.label for item in verto.bandwidth"></select>
</div>
</div>
</div>
</div>
</div>

View File

@ -2,14 +2,14 @@
<div class="col-md-6 centered-block"> <div class="col-md-6 centered-block">
<div class="card"> <div class="card">
<div class="card-body text-center"> <div class="card-body text-center">
<h2>Loading</h2> <h2>{{ 'LOADING' | translate }}</h2>
<div class="progress progress-striped active"> <div class="progress progress-striped active">
<div class="progress-bar" ng-class="{'progress-bar-danger': interrupt_next}" style="width: {{ progress_percentage }}%"></div> <div class="progress-bar" ng-class="{'progress-bar-danger': interrupt_next}" style="width: {{ progress_percentage }}%"></div>
</div> </div>
<div ng-bind="message"></div> <div ng-bind="message"></div>
<div class="splash-errors" ng-if="errors.length"> <div class="splash-errors" ng-if="errors.length">
<h4>Errors</h4> <h4>{{ 'ERRORS' | translate }}</h4>
<ul ng-repeat="error in errors"> <ul ng-repeat="error in errors">
<li>{{ ::error }}</li> <li>{{ ::error }}</li>
</ul> </ul>

View File

@ -2,28 +2,28 @@
<div class="video-wrapper"> <div class="video-wrapper">
<div class="video-hover-buttons" ng-show="verto.data.callState == 'active' && !watcher"> <div class="video-hover-buttons" ng-show="verto.data.callState == 'active' && !watcher">
<div id="moderator-tools" ng-show="verto.data.confRole == 'moderator'"> <div id="moderator-tools" ng-show="verto.data.confRole == 'moderator'">
<button tooltip-placement="bottom" tooltip-title="Play" uib-tooltip="Play" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_PLAY' | translate}}" uib-tooltip="{{'MESSAGE_PLAY' | translate}}"
class="btn btn-material-blue-900" ng-click="play()"> class="btn btn-material-blue-900" ng-click="play()">
<i class="mdi-av-play-circle-outline"></i> <i class="mdi-av-play-circle-outline"></i>
</button> </button>
<button tooltip-placement="bottom" tooltip-title="Stop" uib-tooltip="Stop" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_STOP' | translate}}" uib-tooltip="{{'MESSAGE_STOP' | translate}}"
class="btn btn-material-blue-900" ng-click="stop()"> class="btn btn-material-blue-900" ng-click="stop()">
<i class="mdi-av-stop"></i> <i class="mdi-av-stop"></i>
</button> </button>
<button tooltip-placement="bottom" tooltip-title="Record" uib-tooltip="Record" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_RECORD' | translate}}" uib-tooltip="{{'MESSAGE_RECORD' | translate}}"
class="btn btn-material-blue-900" ng-click="record()"> class="btn btn-material-blue-900" ng-click="record()">
<i class="mdi-toggle-radio-button-on"></i> <i class="mdi-toggle-radio-button-on"></i>
</button> </button>
<button tooltip-placement="bottom" tooltip-title="Stop Record" uib-tooltip="Stop Record" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_STOP_RECORD' | translate}}" uib-tooltip="{{'MESSAGE_STOP_RECORD' | translate}}"
class="btn btn-material-blue-900" ng-click="stopRecord()"> class="btn btn-material-blue-900" ng-click="stopRecord()">
<i class="mdi-image-switch-camera"></i> <i class="mdi-image-switch-camera"></i>
</button> </button>
<button tooltip-placement="bottom" tooltip-title="Snapshot" uib-tooltip="Snapshot" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_SNAPSHOT' | translate}}" uib-tooltip="{{'MESSAGE_SNAPSHOT' | translate}}"
tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="snapshot()"> tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="snapshot()">
<i class="mdi-image-photo-camera"></i> <i class="mdi-image-photo-camera"></i>
</button> </button>
<div class="btn-group"> <div class="btn-group">
<button tooltip-placement="bottom" tooltip-title="Video Mode" uib-tooltip="Video Mode" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_VIDEO_MODE' | translate}}" uib-tooltip="{{'MESSAGE_VIDEO_MODE' | translate}}"
type="button" class="btn btn-material-blue-900 dropdown-toggle" type="button" class="btn btn-material-blue-900 dropdown-toggle"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="mdi-action-view-module"></i> <i class="mdi-action-view-module"></i>
@ -37,28 +37,28 @@
</div> </div>
</div> </div>
<div class="user-tools"> <div class="user-tools">
<button tooltip-placement="bottom" tooltip-title="(un)Mute Mic" uib-tooltip="(un)Mute Mic" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_MUTE_MIC' | translate}}" uib-tooltip="{{'MESSAGE_MUTE_MIC' | translate}}"
class="btn btn-material-blue-900" ng-click="muteMic(cbMuteMic)"> class="btn btn-material-blue-900" ng-click="muteMic(cbMuteMic)">
<i class="" ng-class="{'mdi-av-mic': !verto.data.mutedMic, 'mdi-av-mic-off': verto.data.mutedMic}"></i> <i class="" ng-class="{'mdi-av-mic': !verto.data.mutedMic, 'mdi-av-mic-off': verto.data.mutedMic}"></i>
</button> </button>
<button tooltip-placement="bottom" tooltip-title="(un)Mute Video" uib-tooltip="(un)Mute Video" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_MUTE_VIDEO' | translate}}" uib-tooltip="{{'MESSAGE_MUTE_VIDEO' | translate}}"
class="btn btn-material-blue-900" ng-click="muteVideo(cbMuteVideo)" ng-if="verto.data.canVideo"> class="btn btn-material-blue-900" ng-click="muteVideo(cbMuteVideo)" ng-if="verto.data.canVideo">
<i class="" ng-class="{'mdi-av-videocam': !verto.data.mutedVideo, 'mdi-av-videocam-off': verto.data.mutedVideo}"></i> <i class="" ng-class="{'mdi-av-videocam': !verto.data.mutedVideo, 'mdi-av-videocam-off': verto.data.mutedVideo}"></i>
</button> </button>
<button tooltip-placement="bottom" tooltip-title="Toggle Fullscreen Mode" uib-tooltip="Toggle Fullscreen Mode" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_FULLSCREEN' | translate}}" uib-tooltip="{{'MESSAGE_FULLSCREEN' | translate}}"
class="btn btn-material-blue-900" ng-click="goFullscreen()"> class="btn btn-material-blue-900" ng-click="goFullscreen()">
<i class="" ng-class="{'mdi-navigation-fullscreen': !fullscreenEnabled, 'mdi-navigation-fullscreen-exit': fullscreenEnabled}"></i> <i class="" ng-class="{'mdi-navigation-fullscreen': !fullscreenEnabled, 'mdi-navigation-fullscreen-exit': fullscreenEnabled}"></i>
</button> </button>
<button tooltip-placement="bottom" tooltip-title="Screenshare" uib-tooltip="Screenshare" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_SCREENSHARE' | translate}}" uib-tooltip="{{'MESSAGE_SCREENSHARE' | translate}}"
class="btn btn-material-blue-900" ng-click="screenshare()"> class="btn btn-material-blue-900" ng-click="screenshare()">
<i class="mdi-hardware-desktop-windows"></i> <i class="mdi-hardware-desktop-windows"></i>
</button> </button>
<button tooltip-placement="bottom" tooltip-title="Open/Close Chat" uib-tooltip="Open/Close Chat" <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_OPEN_CLOSE_CHAT' | translate}}" uib-tooltip="{{'MESSAGE_OPEN_CLOSE_CHAT' | translate}}"
class="btn btn-material-blue-900" ng-click="toggleChat()" ng-show="fullscreenEnabled"> class="btn btn-material-blue-900" ng-click="toggleChat()" ng-show="fullscreenEnabled">
<i class="mdi-communication-chat"></i> <i class="mdi-communication-chat"></i>
</button> </button>
<div class="btn-group"> <div class="btn-group">
<button tooltip-placement="bottom" tooltip-title="Speaker" uib-tooltips="Speaker" type="button" class="btn btn-material-blue-900 dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_SPEAKER' | translate}}" uib-tooltips="{{'MESSAGE_SPEAKER' | translate}}" type="button" class="btn btn-material-blue-900 dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="mdi-hardware-headset"></i> <i class="mdi-hardware-headset"></i>
<span class="caret"></span> <span class="caret"></span>
</button> </button>
@ -69,7 +69,7 @@
</ul> </ul>
</div> </div>
<div class="btn-group" ng-show="conf.canvasCount > 1"> <div class="btn-group" ng-show="conf.canvasCount > 1">
<button tooltip-placement="bottom" tooltip-title="Popup" uib-tooltips="Popup" type="button" class="btn btn-material-blue-900 dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button tooltip-placement="bottom" tooltip-title="{{'MESSAGE_POPUP' | translate}}" uib-tooltips="{{'MESSAGE_POPUP' | translate}}" type="button" class="btn btn-material-blue-900 dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="mdi-image-filter-none"></i> <i class="mdi-image-filter-none"></i>
<span class="caret"></span> <span class="caret"></span>
</button> </button>
@ -96,9 +96,9 @@
</div> </div>
</div> </div>
<div class="col-md-6 col-xs-6 text-right"> <div class="col-md-6 col-xs-6 text-right">
<button class="btn btn-danger" ng-click="hangup()"> <button class="btn btn-danger" ng-click="hangup()" translate>
<i class="mdi-communication-call-end"></i> <i class="mdi-communication-call-end"></i>
{{ watcher ? 'Close' : 'End Call' }} {{ watcher ? 'BUTTON_CLOSE' : 'BUTTON_END_CALL' }}
</button> </button>
</div> </div>
</div> </div>

View File

@ -2,8 +2,8 @@
angular angular
.module('storageService') .module('storageService')
.service('splashscreen', ['$rootScope', '$q', 'storage', 'config', 'verto', .service('splashscreen', ['$rootScope', '$q', 'storage', 'config', 'verto', '$translate',
function($rootScope, $q, storage, config, verto) { function($rootScope, $q, storage, config, verto, $translate) {
var checkBrowser = function() { var checkBrowser = function() {
return $q(function(resolve, reject) { return $q(function(resolve, reject) {
@ -12,16 +12,15 @@
'activity': activity, 'activity': activity,
'soft': false, 'soft': false,
'status': 'success', 'status': 'success',
'message': 'Checking browser compability.' 'message': $translate.instant('BROWSER_COMPATIBILITY')
}; };
navigator.getUserMedia = navigator.getUserMedia || navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia; navigator.mozGetUserMedia;
if (!navigator.getUserMedia) { if (!navigator.getUserMedia) {
result['status'] = 'error'; result['status'] = 'error';
result['message'] = 'Error: browser doesn\'t support WebRTC.'; result['message'] = $translate.instant('BROWSER_WITHOUT_WEBRTC');
reject(result); reject(result);
} }
@ -37,13 +36,13 @@
'activity': activity, 'activity': activity,
'soft': false, 'soft': false,
'status': 'success', 'status': 'success',
'message': 'Checking media permissions' 'message': $translate.instant('CHECK_PERMISSION_MEDIA')
}; };
verto.mediaPerm(function(status) { verto.mediaPerm(function(status) {
if(!status) { if(!status) {
result['status'] = 'error'; result['status'] = 'error';
result['message'] = 'Error: Media Permission Denied'; result['message'] = $translate.instant('ERROR_PERMISSION_MEDIA');
verto.data.mediaPerm = false; verto.data.mediaPerm = false;
reject(result); reject(result);
} }
@ -60,7 +59,7 @@
'status': 'success', 'status': 'success',
'soft': true, 'soft': true,
'activity': activity, 'activity': activity,
'message': 'Refresh Media Devices.' 'message': $translate.instant('REFRESH_MEDIA_DEVICES')
}; };
verto.refreshDevices(function(status) { verto.refreshDevices(function(status) {
@ -79,7 +78,7 @@
'status': 'success', 'status': 'success',
'soft': true, 'soft': true,
'activity': activity, 'activity': activity,
'message': 'Check Connection Speed.' 'message': $translate.instant('CHECK_CONNECTION_SPEED')
}; };
if (storage.data.autoBand && verto.data.instance) { if (storage.data.autoBand && verto.data.instance) {
@ -101,7 +100,7 @@
'status': 'promise', 'status': 'promise',
'soft': true, 'soft': true,
'activity': activity, 'activity': activity,
'message': 'Provisioning configuration.' 'message': $translate.instant('CHECK_PROVISIONING_CONF')
}; };
var configResponse = config.configure(); var configResponse = config.configure();
@ -116,7 +115,7 @@
return result; return result;
} else { } else {
result['status'] = 'error'; result['status'] = 'error';
result['message'] = 'Error: Provision failed.'; result['message'] = $translate.instant('ERROR_PROVISIONING_CONF');
return result; return result;
} }
}); });
@ -134,7 +133,7 @@
'status': 'success', 'status': 'success',
'soft': true, 'soft': true,
'activity': activity, 'activity': activity,
'message': 'Checking login.' 'message': $translate.instant('CHECK_LOGIN'),
}; };
if(verto.data.connecting || verto.data.connected) { if(verto.data.connecting || verto.data.connected) {
@ -179,19 +178,19 @@
]; ];
var progress_message = [ var progress_message = [
'Checking browser compability.', $translate.instant('BROWSER_COMPATIBILITY'),
'Checking media permissions', $translate.instant('CHECK_PERMISSION_MEDIA'),
'Refresh Media Devices.', $translate.instant('REFRESH_MEDIA_DEVICES'),
'Provisioning configuration.', $translate.instant('CHECK_PROVISIONING_CONF'),
'Checking login.', $translate.instant('CHECK_LOGIN'),
'Check Connection Speed.' $translate.instant('CHECK_CONNECTION_SPEED'),
]; ];
var getProgressMessage = function(current_progress) { var getProgressMessage = function(current_progress) {
if(progress_message[current_progress] != undefined) { if(progress_message[current_progress] != undefined) {
return progress_message[current_progress]; return progress_message[current_progress];
} else { } else {
return 'Please wait...'; return $translate.instant('PLEASE_WAIT');
} }
}; };

View File

@ -17,10 +17,11 @@
'ui.gravatar', 'ui.gravatar',
'ui.bootstrap', 'ui.bootstrap',
'directive.g+signin', 'directive.g+signin',
'pascalprecht.translate',
]); ]);
vertoApp.config(['$routeProvider', 'gravatarServiceProvider', vertoApp.config(['$routeProvider', 'gravatarServiceProvider', '$translateProvider',
function($routeProvider, gravatarServiceProvider) { function($routeProvider, gravatarServiceProvider, $translateProvider) {
$routeProvider. $routeProvider.
when('/', { when('/', {
title: 'Loading', title: 'Loading',
@ -59,6 +60,47 @@
gravatarServiceProvider.defaults = { gravatarServiceProvider.defaults = {
default: 'mm' // Mystery man as default for missing avatars default: 'mm' // Mystery man as default for missing avatars
}; };
$translateProvider
.useStaticFilesLoader({
prefix: 'locales/locale-',
suffix: '.json'
})
.registerAvailableLanguageKeys(['en', 'it', 'pt', 'fr', 'de', 'es', 'pl', 'ru', 'sv', 'id', 'zh'], {
'en': 'en',
'en_GB': 'en',
'en_US': 'en',
'it': 'it',
'it_IT': 'it',
'fr': 'fr',
'fr_FR': 'fr',
'fr_CA': 'fr',
'pt': 'pt',
'pt_BR': 'pt',
'pt_PT': 'pt',
'de': 'de',
'de_DE': 'de',
'es': 'es',
'es_ES': 'es',
'pl': 'pl',
'pl_PL': 'pl',
'ru': 'ru',
'ru_RU': 'ru',
'sv': 'sv',
'sv_SV': 'sv',
'sv_FI': 'sv',
'id': 'id',
'id_ID': 'id',
'zh': 'zh',
'zh_CN': 'zh',
'zh_TW': 'zh',
'zh_HK': 'zh'
})
.preferredLanguage('en')
.determinePreferredLanguage()
.fallbackLanguage('en')
.useSanitizeValueStrategy(null);
} }
]); ]);

View File

@ -5,9 +5,9 @@
angular angular
.module('vertoControllers') .module('vertoControllers')
.controller('ChatController', ['$scope', '$rootScope', '$http', .controller('ChatController', ['$scope', '$rootScope', '$http',
'$location', '$anchorScroll', '$timeout', 'verto', 'prompt', '$location', '$anchorScroll', '$timeout', 'verto', 'prompt', '$translate',
function($scope, $rootScope, $http, $location, $anchorScroll, $timeout, function($scope, $rootScope, $http, $location, $anchorScroll, $timeout,
verto, prompt) { verto, prompt, $translate) {
console.debug('Executing ChatController.'); console.debug('Executing ChatController.');
function scrollToChatBottom() { function scrollToChatBottom() {
@ -246,7 +246,7 @@
console.log('$scope.confBanner'); console.log('$scope.confBanner');
prompt({ prompt({
title: 'Please insert the banner text', title: $translate.instant('TITLE_INSERT_BANNER'),
input: true, input: true,
label: '', label: '',
value: '', value: '',
@ -263,7 +263,7 @@
return; return;
} }
shortPrompt('Please insert the Canvas Id', function(canvasID) { shortPrompt($translate.instant('TITLE_INSERT_CANVAS_ID'), function(canvasID) {
console.log(memberID, canvasID); console.log(memberID, canvasID);
verto.setCanvasIn(memberID, canvasID); verto.setCanvasIn(memberID, canvasID);
}); });
@ -276,7 +276,7 @@
return; return;
} }
shortPrompt('Please insert the Canvas Id', function(canvasID) { shortPrompt($translate.instant('TITLE_INSERT_CANVAS_ID'), function(canvasID) {
verto.setCanvasOut(memberID, canvasID); verto.setCanvasOut(memberID, canvasID);
}); });
}; };
@ -287,7 +287,7 @@
return; return;
} }
shortPrompt('Please insert the Layer', function(canvasID) { shortPrompt($translate.instant('TITLE_INSERT_LAYER'), function(canvasID) {
verto.setLayer(memberID, canvasID); verto.setLayer(memberID, canvasID);
}); });
}; };
@ -321,10 +321,10 @@
$scope.confTransfer = function(memberID) { $scope.confTransfer = function(memberID) {
console.log('$scope.confTransfer'); console.log('$scope.confTransfer');
prompt({ prompt({
title: 'Transfer party?', title: $translate.instant('TITLE_TRANSFER'),
message: 'To what destination would you like to transfer this call?', message: $translate.instant('MESSAGE_TRANSFER'),
input: true, input: true,
label: 'Destination', label: $translate.instant('LABEL_TRANSFER'),
value: '', value: '',
}).then(function(exten) { }).then(function(exten) {
if (exten) { if (exten) {

View File

@ -89,7 +89,17 @@
if (extension.indexOf('-canvas-') != -1) { if (extension.indexOf('-canvas-') != -1) {
$rootScope.watcher = true; $rootScope.watcher = true;
verto.call($rootScope.dialpadNumber, null, { useCamera: false, useMic: false, caller_id_name: null, userVariables: {}, caller_id_number: null, mirrorInput: false }); verto.call($rootScope.dialpadNumber, null,
{
useCamera: "none",
useMic: "none",
useSpeak: "none",
caller_id_name: null,
userVariables: {},
caller_id_number: null,
mirrorInput: false
}
);
$location.path('/incall'); $location.path('/incall');
return; return;
} }
@ -109,6 +119,7 @@
* Call to the number in the $rootScope.dialpadNumber. * Call to the number in the $rootScope.dialpadNumber.
*/ */
$scope.loading = false; $scope.loading = false;
$scope.cancelled = false;
$rootScope.call = function(extension) { $rootScope.call = function(extension) {
if (!storage.data.testSpeedJoin || !$rootScope.dialpadNumber) { if (!storage.data.testSpeedJoin || !$rootScope.dialpadNumber) {
return call(extension); return call(extension);
@ -116,10 +127,19 @@
$scope.loading = true; $scope.loading = true;
verto.testSpeed(function() { verto.testSpeed(function() {
$scope.loading = false; if ($scope.cancelled) {
call(extension); $scope.cancelled = false;
$scope.loading = false;
return;
} else {
call(extension);
}
}); });
} }
$rootScope.cancel = function() {
$scope.cancelled = true;
}
} }
]); ]);

View File

@ -4,9 +4,9 @@
angular angular
.module('vertoControllers') .module('vertoControllers')
.controller('InCallController', ['$rootScope', '$scope', .controller('InCallController', ['$rootScope', '$scope',
'$http', '$location', '$modal', '$timeout', 'toastr', 'verto', 'storage', 'prompt', 'Fullscreen', '$http', '$location', '$modal', '$timeout', 'toastr', 'verto', 'storage', 'prompt', 'Fullscreen', '$translate',
function($rootScope, $scope, $http, $location, $modal, $timeout, toastr, function($rootScope, $scope, $http, $location, $modal, $timeout, toastr,
verto, storage, prompt, Fullscreen) { verto, storage, prompt, Fullscreen, $translate) {
console.debug('Executing InCallController.'); console.debug('Executing InCallController.');
$scope.layout = null; $scope.layout = null;
@ -55,8 +55,8 @@
*/ */
$scope.videoCall = function() { $scope.videoCall = function() {
prompt({ prompt({
title: 'Would you like to activate video for this call?', title: $translate.instant('TITLE_ENABLE_VIDEO'),
message: 'Video will be active during the next calls.' message: $translate.instant('MESSAGE_ENABLE_VIDEO')
}).then(function() { }).then(function() {
storage.data.videoCall = true; storage.data.videoCall = true;
$scope.callTemplate = 'partials/video_call.html'; $scope.callTemplate = 'partials/video_call.html';

View File

@ -4,7 +4,7 @@
angular angular
.module('vertoControllers') .module('vertoControllers')
.controller('MainController', .controller('MainController',
function($scope, $rootScope, $location, $modal, $timeout, $q, verto, storage, CallHistory, toastr, Fullscreen, prompt, eventQueue) { function($scope, $rootScope, $location, $modal, $timeout, $q, verto, storage, CallHistory, toastr, Fullscreen, prompt, eventQueue, $translate) {
console.debug('Executing MainController.'); console.debug('Executing MainController.');
@ -123,8 +123,8 @@
if (verto.data.call) { if (verto.data.call) {
prompt({ prompt({
title: 'Oops, Active Call in Course.', title: $translate.instant('TITLE_ACTIVE_CALL'),
message: 'It seems that you are in a call. Do you want to hang up?' message: $translate.instant('MESSAGE_ACTIVE_CALL_HANGUP')
}).then(function() { }).then(function() {
disconnect(); disconnect();
}); });
@ -278,6 +278,12 @@
angular.element('#wrapper').addClass('toggled'); angular.element('#wrapper').addClass('toggled');
}; };
$scope.toggleSettings = function() {
var settingsEl = angular.element(document.querySelector('#settings'));
settingsEl.toggleClass('toggled');
$rootScope.$emit('toggledSettings', settingsEl.hasClass('toggled'));
};
$scope.goFullscreen = function() { $scope.goFullscreen = function() {
if (storage.data.userStatus !== 'connected') { if (storage.data.userStatus !== 'connected') {
return; return;
@ -320,8 +326,8 @@
return $q(function(resolve, reject) { return $q(function(resolve, reject) {
if (storage.data.askRecoverCall) { if (storage.data.askRecoverCall) {
prompt({ prompt({
title: 'Oops, Active Call in Course.', title: $translate.instant('TITLE_ACTIVE_CALL'),
message: 'It seems you were in a call before leaving the last time. Wanna go back to that?' message: $translate.instant('MESSAGE_ACTIVE_CALL_BACK')
}).then(function() { }).then(function() {
console.log('redirect to incall page'); console.log('redirect to incall page');
$location.path('/incall'); $location.path('/incall');
@ -417,8 +423,8 @@
storage.data.mutedMic = false; storage.data.mutedMic = false;
prompt({ prompt({
title: 'Incoming Call', title: $translate.instant('TITLE_INCOMING_CALL'),
message: 'from ' + data message: $translate.instant('MESSAGE_INCOMING_CALL') + data
}).then(function() { }).then(function() {
var call_start = new Date(storage.data.call_start); var call_start = new Date(storage.data.call_start);
$rootScope.start_time = call_start; $rootScope.start_time = call_start;
@ -444,7 +450,7 @@
*/ */
$scope.hangup = function() { $scope.hangup = function() {
if (!verto.data.call) { if (!verto.data.call) {
toastr.warning('There is no call to hangup.'); toastr.warning($translate.instant('MESSAGE_NO_HANGUP_CALL'));
$location.path('/dialpad'); $location.path('/dialpad');
return; return;
} }
@ -495,7 +501,7 @@
}; };
$scope.play = function() { $scope.play = function() {
var file = $scope.promptInput('Please, enter filename', '', 'File', var file = $scope.promptInput($translate.instant('MESSAGE_ENTER_FILENAME'), '', 'File',
function(file) { function(file) {
verto.data.conf.play(file); verto.data.conf.play(file);
console.log('play file :', file); console.log('play file :', file);
@ -508,7 +514,7 @@
}; };
$scope.record = function() { $scope.record = function() {
var file = $scope.promptInput('Please, enter filename', '', 'File', var file = $scope.promptInput($translate.instant('MESSAGE_ENTER_FILENAME'), '', 'File',
function(file) { function(file) {
verto.data.conf.record(file); verto.data.conf.record(file);
console.log('recording file :', file); console.log('recording file :', file);
@ -520,7 +526,7 @@
}; };
$scope.snapshot = function() { $scope.snapshot = function() {
var file = $scope.promptInput('Please, enter filename', '', 'File', var file = $scope.promptInput($translate.instant('MESSAGE_ENTER_FILENAME'), '', 'File',
function(file) { function(file) {
verto.data.conf.snapshot(file); verto.data.conf.snapshot(file);
console.log('snapshot file :', file); console.log('snapshot file :', file);

View File

@ -4,9 +4,9 @@
angular angular
.module('vertoControllers') .module('vertoControllers')
.controller('PreviewController', ['$rootScope', '$scope', .controller('PreviewController', ['$rootScope', '$scope',
'$http', '$location', '$modal', '$timeout', 'toastr', 'verto', 'storage', 'prompt', 'Fullscreen', '$http', '$location', '$modal', '$timeout', 'toastr', 'verto', 'storage', 'prompt', 'Fullscreen', '$translate',
function($rootScope, $scope, $http, $location, $modal, $timeout, toastr, function($rootScope, $scope, $http, $location, $modal, $timeout, toastr,
verto, storage, prompt, Fullscreen) { verto, storage, prompt, Fullscreen, $translate) {
$scope.storage = storage; $scope.storage = storage;
console.debug('Executing PreviewController.'); console.debug('Executing PreviewController.');
@ -87,8 +87,8 @@
$scope.videoCall = function() { $scope.videoCall = function() {
prompt({ prompt({
title: 'Would you like to activate video for this call?', title: $translate.instant('TITLE_ENABLE_VIDEO'),
message: 'Video will be active during the next calls.' message: $translate.instant('MESSAGE_ENABLE_VIDEO')
}).then(function() { }).then(function() {
storage.data.videoCall = true; storage.data.videoCall = true;
$scope.callTemplate = 'partials/video_call.html'; $scope.callTemplate = 'partials/video_call.html';

View File

@ -3,16 +3,25 @@
angular angular
.module('vertoControllers') .module('vertoControllers')
.controller('ModalSettingsController', ['$scope', '$http', .controller('SettingsController', ['$scope', '$http',
'$location', '$modalInstance', '$rootScope', 'storage', 'verto', '$location', '$rootScope', 'storage', 'verto', '$translate', 'toastr',
function($scope, $http, $location, $modalInstance, $rootScope, storage, verto) { function($scope, $http, $location, $rootScope, storage, verto, $translate, toastr) {
console.debug('Executing ModalSettingsController.'); console.debug('Executing ModalSettingsController.');
$.material.init();
$scope.speakerFeature = typeof document.getElementById('webcam').sinkId !== 'undefined';
$scope.storage = storage; $scope.storage = storage;
$scope.verto = verto; $scope.verto = verto;
$scope.mydata = angular.copy(storage.data); $scope.mydata = angular.copy(storage.data);
$scope.speakerFeature = typeof document.getElementById('webcam').sinkId !== 'undefined'; $rootScope.$on('toggledSettings', function(e, status) {
if (status) {
$scope.mydata = angular.copy(storage.data);
} else {
$scope.ok();
}
});
$scope.ok = function() { $scope.ok = function() {
if ($scope.mydata.selectedSpeaker != storage.data.selectedSpeaker) { if ($scope.mydata.selectedSpeaker != storage.data.selectedSpeaker) {
@ -24,17 +33,24 @@
if (storage.data.autoBand) { if (storage.data.autoBand) {
$scope.testSpeed(); $scope.testSpeed();
} }
$modalInstance.close('Ok.');
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
}; };
$scope.refreshDeviceList = function() { $scope.refreshDeviceList = function() {
return verto.refreshDevices(); return verto.refreshDevices();
}; };
$scope.showPreview = function() {
var settingsEl = angular.element(document.querySelector('#settings'));
settingsEl.toggleClass('toggled');
if (!verto.data.call) {
$location.path('/preview');
return;
}
else {
toastr.warning($translate.instant('MESSAGE_DISPLAY_SETTINGS'));
}
};
$scope.testSpeed = function() { $scope.testSpeed = function() {
return verto.testSpeed(cb); return verto.testSpeed(cb);
@ -49,7 +65,6 @@
if (confirm('Factory Reset Settings?')) { if (confirm('Factory Reset Settings?')) {
storage.factoryReset(); storage.factoryReset();
$scope.logout(); $scope.logout();
$modalInstance.close('Ok.');
window.location.reload(); window.location.reload();
}; };
}; };

View File

@ -6,7 +6,7 @@ function getCodecPayloadType(sdpLine){var pattern=new RegExp('a=rtpmap:(\\d+) \\
function setDefaultCodec(mLine,payload){var elements=mLine.split(' ');var newLine=[];var index=0;for(var i=0;i<elements.length;i++){if(index===3){newLine[index++]=payload;} function setDefaultCodec(mLine,payload){var elements=mLine.split(' ');var newLine=[];var index=0;for(var i=0;i<elements.length;i++){if(index===3){newLine[index++]=payload;}
if(elements[i]!==payload)newLine[index++]=elements[i];} if(elements[i]!==payload)newLine[index++]=elements[i];}
return newLine.join(' ');} return newLine.join(' ');}
$.FSRTC=function(options){this.options=$.extend({useVideo:null,useStereo:false,userData:null,localVideo:null,screenShare:false,useCamera:"any",iceServers:false,videoParams:{},audioParams:{},callbacks:{onICEComplete:function(){},onICE:function(){},onOfferSDP:function(){}},},options);this.audioEnabled=true;this.videoEnabled=true;this.mediaData={SDP:null,profile:{},candidateList:[]};if(moz){this.constraints={offerToReceiveAudio:true,offerToReceiveVideo:this.options.useVideo?true:false,};}else{this.constraints={optional:[{'DtlsSrtpKeyAgreement':'true'}],mandatory:{OfferToReceiveAudio:true,OfferToReceiveVideo:this.options.useVideo?true:false,}};} $.FSRTC=function(options){this.options=$.extend({useVideo:null,useStereo:false,userData:null,localVideo:null,screenShare:false,useCamera:"any",iceServers:false,videoParams:{},audioParams:{},callbacks:{onICEComplete:function(){},onICE:function(){},onOfferSDP:function(){}},},options);this.audioEnabled=true;this.videoEnabled=true;this.mediaData={SDP:null,profile:{},candidateList:[]};if(moz){this.constraints={offerToReceiveAudio:this.options.useSpeak==="none"?false:true,offerToReceiveVideo:this.options.useVideo?true:false,};}else{this.constraints={optional:[{'DtlsSrtpKeyAgreement':'true'}],mandatory:{OfferToReceiveAudio:this.options.useSpeak==="none"?false:true,OfferToReceiveVideo:this.options.useVideo?true:false,}};}
if(self.options.useVideo){self.options.useVideo.style.display='none';} if(self.options.useVideo){self.options.useVideo.style.display='none';}
setCompat();checkCompat();};$.FSRTC.validRes=[];$.FSRTC.prototype.useVideo=function(obj,local){var self=this;if(obj){self.options.useVideo=obj;self.options.localVideo=local;if(moz){self.constraints.offerToReceiveVideo=true;}else{self.constraints.mandatory.OfferToReceiveVideo=true;}}else{self.options.useVideo=null;self.options.localVideo=null;if(moz){self.constraints.offerToReceiveVideo=false;}else{self.constraints.mandatory.OfferToReceiveVideo=false;}} setCompat();checkCompat();};$.FSRTC.validRes=[];$.FSRTC.prototype.useVideo=function(obj,local){var self=this;if(obj){self.options.useVideo=obj;self.options.localVideo=local;if(moz){self.constraints.offerToReceiveVideo=true;}else{self.constraints.mandatory.OfferToReceiveVideo=true;}}else{self.options.useVideo=null;self.options.localVideo=null;if(moz){self.constraints.offerToReceiveVideo=false;}else{self.constraints.mandatory.OfferToReceiveVideo=false;}}
if(self.options.useVideo){self.options.useVideo.style.display='none';}};$.FSRTC.prototype.useStereo=function(on){var self=this;self.options.useStereo=on;};$.FSRTC.prototype.stereoHack=function(sdp){var self=this;if(!self.options.useStereo){return sdp;} if(self.options.useVideo){self.options.useVideo.style.display='none';}};$.FSRTC.prototype.useStereo=function(on){var self=this;self.options.useStereo=on;};$.FSRTC.prototype.stereoHack=function(sdp){var self=this;if(!self.options.useStereo){return sdp;}
@ -218,14 +218,14 @@ dt.fnAdjustColumnSizing();break;case"modify":if(!args.data){return;}
dt.fnUpdate(genRow(args.data),index);dt.fnAdjustColumnSizing();break;case"del":dt.fnDeleteRow(index);dt.fnAdjustColumnSizing();break;case"clear":dt.fnClearTable();break;case"reorder":dt.fnClearTable();dt.fnAddData(genArray(obj));break;case"hide":jq.hide();break;case"show":jq.show();break;}}catch(err){console.error("ERROR: "+err);iserr++;} dt.fnUpdate(genRow(args.data),index);dt.fnAdjustColumnSizing();break;case"del":dt.fnDeleteRow(index);dt.fnAdjustColumnSizing();break;case"clear":dt.fnClearTable();break;case"reorder":dt.fnClearTable();dt.fnAddData(genArray(obj));break;case"hide":jq.hide();break;case"show":jq.show();break;}}catch(err){console.error("ERROR: "+err);iserr++;}
if(iserr){obj.errs++;if(obj.errs<3){obj.bootstrap(obj.user_obj);}}else{obj.errs=0;}};la.onChange(la,{action:"init"});};var CONFMAN_SERNO=1;$.verto.conf=function(verto,params){var conf=this;conf.params=$.extend({dialog:null,hasVid:false,laData:null,onBroadcast:null,onLaChange:null,onLaRow:null},params);conf.verto=verto;conf.serno=CONFMAN_SERNO++;createMainModeratorMethods();verto.subscribe(conf.params.laData.modChannel,{handler:function(v,e){if(conf.params.onBroadcast){conf.params.onBroadcast(verto,conf,e.data);}}});verto.subscribe(conf.params.laData.chatChannel,{handler:function(v,e){if(typeof(conf.params.chatCallback)==="function"){conf.params.chatCallback(v,e);}}});};$.verto.conf.prototype.modCommand=function(cmd,id,value){var conf=this;conf.verto.rpcClient.call("verto.broadcast",{"eventChannel":conf.params.laData.modChannel,"data":{"application":"conf-control","command":cmd,"id":id,"value":value}});};$.verto.conf.prototype.destroy=function(){var conf=this;conf.destroyed=true;conf.params.onBroadcast(conf.verto,conf,'destroy');if(conf.params.laData.modChannel){conf.verto.unsubscribe(conf.params.laData.modChannel);} if(iserr){obj.errs++;if(obj.errs<3){obj.bootstrap(obj.user_obj);}}else{obj.errs=0;}};la.onChange(la,{action:"init"});};var CONFMAN_SERNO=1;$.verto.conf=function(verto,params){var conf=this;conf.params=$.extend({dialog:null,hasVid:false,laData:null,onBroadcast:null,onLaChange:null,onLaRow:null},params);conf.verto=verto;conf.serno=CONFMAN_SERNO++;createMainModeratorMethods();verto.subscribe(conf.params.laData.modChannel,{handler:function(v,e){if(conf.params.onBroadcast){conf.params.onBroadcast(verto,conf,e.data);}}});verto.subscribe(conf.params.laData.chatChannel,{handler:function(v,e){if(typeof(conf.params.chatCallback)==="function"){conf.params.chatCallback(v,e);}}});};$.verto.conf.prototype.modCommand=function(cmd,id,value){var conf=this;conf.verto.rpcClient.call("verto.broadcast",{"eventChannel":conf.params.laData.modChannel,"data":{"application":"conf-control","command":cmd,"id":id,"value":value}});};$.verto.conf.prototype.destroy=function(){var conf=this;conf.destroyed=true;conf.params.onBroadcast(conf.verto,conf,'destroy');if(conf.params.laData.modChannel){conf.verto.unsubscribe(conf.params.laData.modChannel);}
if(conf.params.laData.chatChannel){conf.verto.unsubscribe(conf.params.laData.chatChannel);}};function createMainModeratorMethods(){$.verto.conf.prototype.listVideoLayouts=function(){this.modCommand("list-videoLayouts",null,null);};$.verto.conf.prototype.play=function(file){this.modCommand("play",null,file);};$.verto.conf.prototype.stop=function(){this.modCommand("stop",null,"all");};$.verto.conf.prototype.record=function(file){this.modCommand("recording",null,["start",file]);};$.verto.conf.prototype.stopRecord=function(){this.modCommand("recording",null,["stop","all"]);};$.verto.conf.prototype.snapshot=function(file){if(!this.params.hasVid){throw'Conference has no video';} if(conf.params.laData.chatChannel){conf.verto.unsubscribe(conf.params.laData.chatChannel);}};function createMainModeratorMethods(){$.verto.conf.prototype.listVideoLayouts=function(){this.modCommand("list-videoLayouts",null,null);};$.verto.conf.prototype.play=function(file){this.modCommand("play",null,file);};$.verto.conf.prototype.stop=function(){this.modCommand("stop",null,"all");};$.verto.conf.prototype.record=function(file){this.modCommand("recording",null,["start",file]);};$.verto.conf.prototype.stopRecord=function(){this.modCommand("recording",null,["stop","all"]);};$.verto.conf.prototype.snapshot=function(file){if(!this.params.hasVid){throw'Conference has no video';}
this.modCommand("vid-write-png",null,file);};$.verto.conf.prototype.setVideoLayout=function(layout){if(!this.params.hasVid){throw'Conference has no video';} this.modCommand("vid-write-png",null,file);};$.verto.conf.prototype.setVideoLayout=function(layout,canvasID){if(!this.params.hasVid){throw'Conference has no video';}
this.modCommand("vid-layout",null,layout);};$.verto.conf.prototype.kick=function(memberID){this.modCommand("kick",parseInt(memberID));};$.verto.conf.prototype.muteMic=function(memberID){this.modCommand("tmute",parseInt(memberID));};$.verto.conf.prototype.muteVideo=function(memberID){if(!this.params.hasVid){throw'Conference has no video';} if(canvasID){this.modCommand("vid-layout",null,[layout,canvasID]);}else{this.modCommand("vid-layout",null,layout);}};$.verto.conf.prototype.kick=function(memberID){this.modCommand("kick",parseInt(memberID));};$.verto.conf.prototype.muteMic=function(memberID){this.modCommand("tmute",parseInt(memberID));};$.verto.conf.prototype.muteVideo=function(memberID){if(!this.params.hasVid){throw'Conference has no video';}
this.modCommand("tvmute",parseInt(memberID));};$.verto.conf.prototype.presenter=function(memberID){if(!this.params.hasVid){throw'Conference has no video';} this.modCommand("tvmute",parseInt(memberID));};$.verto.conf.prototype.presenter=function(memberID){if(!this.params.hasVid){throw'Conference has no video';}
this.modCommand("vid-res-id",parseInt(memberID),"presenter");};$.verto.conf.prototype.videoFloor=function(memberID){if(!this.params.hasVid){throw'Conference has no video';} this.modCommand("vid-res-id",parseInt(memberID),"presenter");};$.verto.conf.prototype.videoFloor=function(memberID){if(!this.params.hasVid){throw'Conference has no video';}
this.modCommand("vid-floor",parseInt(memberID),"force");};$.verto.conf.prototype.banner=function(memberID,text){if(!this.params.hasVid){throw'Conference has no video';} this.modCommand("vid-floor",parseInt(memberID),"force");};$.verto.conf.prototype.banner=function(memberID,text){if(!this.params.hasVid){throw'Conference has no video';}
this.modCommand("vid-banner",parseInt(memberID),escape(text));};$.verto.conf.prototype.volumeDown=function(memberID){this.modCommand("volume_out",parseInt(memberID),"down");};$.verto.conf.prototype.volumeUp=function(memberID){this.modCommand("volume_out",parseInt(memberID),"up");};$.verto.conf.prototype.gainDown=function(memberID){this.modCommand("volume_in",parseInt(memberID),"down");};$.verto.conf.prototype.gainUp=function(memberID){this.modCommand("volume_in",parseInt(memberID),"up");};$.verto.conf.prototype.transfer=function(memberID,exten){this.modCommand("transfer",parseInt(memberID),exten);};$.verto.conf.prototype.sendChat=function(message,type){var conf=this;conf.verto.rpcClient.call("verto.broadcast",{"eventChannel":conf.params.laData.chatChannel,"data":{"action":"send","message":message,"type":type}});};} this.modCommand("vid-banner",parseInt(memberID),escape(text));};$.verto.conf.prototype.volumeDown=function(memberID){this.modCommand("volume_out",parseInt(memberID),"down");};$.verto.conf.prototype.volumeUp=function(memberID){this.modCommand("volume_out",parseInt(memberID),"up");};$.verto.conf.prototype.gainDown=function(memberID){this.modCommand("volume_in",parseInt(memberID),"down");};$.verto.conf.prototype.gainUp=function(memberID){this.modCommand("volume_in",parseInt(memberID),"up");};$.verto.conf.prototype.transfer=function(memberID,exten){this.modCommand("transfer",parseInt(memberID),exten);};$.verto.conf.prototype.sendChat=function(message,type){var conf=this;conf.verto.rpcClient.call("verto.broadcast",{"eventChannel":conf.params.laData.chatChannel,"data":{"action":"send","message":message,"type":type}});};}
$.verto.modfuncs={};$.verto.confMan=function(verto,params){var confMan=this;confMan.params=$.extend({tableID:null,statusID:null,mainModID:null,dialog:null,hasVid:false,laData:null,onBroadcast:null,onLaChange:null,onLaRow:null},params);confMan.verto=verto;confMan.serno=CONFMAN_SERNO++;confMan.canvasCount=confMan.params.laData.canvasCount;function genMainMod(jq){var play_id="play_"+confMan.serno;var stop_id="stop_"+confMan.serno;var recording_id="recording_"+confMan.serno;var snapshot_id="snapshot_"+confMan.serno;var rec_stop_id="recording_stop"+confMan.serno;var div_id="confman_"+confMan.serno;var html="<div id='"+div_id+"'><br>"+"<button class='ctlbtn' id='"+play_id+"'>Play</button>"+"<button class='ctlbtn' id='"+stop_id+"'>Stop</button>"+"<button class='ctlbtn' id='"+recording_id+"'>Record</button>"+"<button class='ctlbtn' id='"+rec_stop_id+"'>Record Stop</button>"+ $.verto.modfuncs={};$.verto.confMan=function(verto,params){var confMan=this;confMan.params=$.extend({tableID:null,statusID:null,mainModID:null,dialog:null,hasVid:false,laData:null,onBroadcast:null,onLaChange:null,onLaRow:null},params);confMan.verto=verto;confMan.serno=CONFMAN_SERNO++;confMan.canvasCount=confMan.params.laData.canvasCount;function genMainMod(jq){var play_id="play_"+confMan.serno;var stop_id="stop_"+confMan.serno;var recording_id="recording_"+confMan.serno;var snapshot_id="snapshot_"+confMan.serno;var rec_stop_id="recording_stop"+confMan.serno;var div_id="confman_"+confMan.serno;var html="<div id='"+div_id+"'><br>"+"<button class='ctlbtn' id='"+play_id+"'>Play</button>"+"<button class='ctlbtn' id='"+stop_id+"'>Stop</button>"+"<button class='ctlbtn' id='"+recording_id+"'>Record</button>"+"<button class='ctlbtn' id='"+rec_stop_id+"'>Record Stop</button>"+
(confMan.params.hasVid?"<button class='ctlbtn' id='"+snapshot_id+"'>PNG Snapshot</button>":"")+"<br><br></div>";jq.html(html);$.verto.modfuncs.change_video_layout=function(id,canvas_id){var val=$("#"+id+" option:selected").text();if(val!=="none"){confMan.modCommand("vid-layout",null,[val,canvas_id]);}};if(confMan.params.hasVid){for(var j=0;j<confMan.canvasCount;j++){var vlayout_id="confman_vid_layout_"+j+"_"+confMan.serno;var vlselect_id="confman_vl_select_"+j+"_"+confMan.serno;var vlhtml="<div id='"+vlayout_id+"'><br>"+"<b>Video Layout Canvas "+(j+1)+"</b> <select onChange='$.verto.modfuncs.change_video_layout(\""+vlayout_id+"\", \""+j+"\")' id='"+vlselect_id+"'></select> "+"<br><br></div>";jq.append(vlhtml);} (confMan.params.hasVid?"<button class='ctlbtn' id='"+snapshot_id+"'>PNG Snapshot</button>":"")+"<br><br></div>";jq.html(html);$.verto.modfuncs.change_video_layout=function(id,canvas_id){var val=$("#"+id+" option:selected").text();if(val!=="none"){confMan.modCommand("vid-layout",null,[val,canvas_id]);}};if(confMan.params.hasVid){for(var j=0;j<confMan.canvasCount;j++){var vlayout_id="confman_vid_layout_"+j+"_"+confMan.serno;var vlselect_id="confman_vl_select_"+j+"_"+confMan.serno;var vlhtml="<div id='"+vlayout_id+"'><br>"+"<b>Video Layout Canvas "+(j+1)+"</b> <select onChange='$.verto.modfuncs.change_video_layout(\""+vlayout_id+"\", \""+(j+1)+"\")' id='"+vlselect_id+"'></select> "+"<br><br></div>";jq.append(vlhtml);}
$("#"+snapshot_id).click(function(){var file=prompt("Please enter file name","");if(file){confMan.modCommand("vid-write-png",null,file);}});} $("#"+snapshot_id).click(function(){var file=prompt("Please enter file name","");if(file){confMan.modCommand("vid-write-png",null,file);}});}
$("#"+play_id).click(function(){var file=prompt("Please enter file name","");if(file){confMan.modCommand("play",null,file);}});$("#"+stop_id).click(function(){confMan.modCommand("stop",null,"all");});$("#"+recording_id).click(function(){var file=prompt("Please enter file name","");if(file){confMan.modCommand("recording",null,["start",file]);}});$("#"+rec_stop_id).click(function(){confMan.modCommand("recording",null,["stop","all"]);});} $("#"+play_id).click(function(){var file=prompt("Please enter file name","");if(file){confMan.modCommand("play",null,file);}});$("#"+stop_id).click(function(){confMan.modCommand("stop",null,"all");});$("#"+recording_id).click(function(){var file=prompt("Please enter file name","");if(file){confMan.modCommand("recording",null,["start",file]);}});$("#"+rec_stop_id).click(function(){confMan.modCommand("recording",null,["stop","all"]);});}
function genControls(jq,rowid){var x=parseInt(rowid);var kick_id="kick_"+x;var canvas_in_next_id="canvas_in_next_"+x;var canvas_in_prev_id="canvas_in_prev_"+x;var canvas_out_next_id="canvas_out_next_"+x;var canvas_out_prev_id="canvas_out_prev_"+x;var canvas_in_set_id="canvas_in_set_"+x;var canvas_out_set_id="canvas_out_set_"+x;var layer_set_id="layer_set_"+x;var layer_next_id="layer_next_"+x;var layer_prev_id="layer_prev_"+x;var tmute_id="tmute_"+x;var tvmute_id="tvmute_"+x;var vbanner_id="vbanner_"+x;var tvpresenter_id="tvpresenter_"+x;var tvfloor_id="tvfloor_"+x;var box_id="box_"+x;var gainup_id="gain_in_up"+x;var gaindn_id="gain_in_dn"+x;var volup_id="vol_in_up"+x;var voldn_id="vol_in_dn"+x;var transfer_id="transfer"+x;var html="<div id='"+box_id+"'>";html+="<b>General Controls</b><hr noshade>";html+="<button class='ctlbtn' id='"+kick_id+"'>Kick</button>"+"<button class='ctlbtn' id='"+tmute_id+"'>Mute</button>"+"<button class='ctlbtn' id='"+gainup_id+"'>Gain -</button>"+"<button class='ctlbtn' id='"+gaindn_id+"'>Gain +</button>"+"<button class='ctlbtn' id='"+voldn_id+"'>Vol -</button>"+"<button class='ctlbtn' id='"+volup_id+"'>Vol +</button>"+"<button class='ctlbtn' id='"+transfer_id+"'>Transfer</button>";if(confMan.params.hasVid){html+="<br><br><b>Video Controls</b><hr noshade>";html+="<button class='ctlbtn' id='"+tvmute_id+"'>VMute</button>"+"<button class='ctlbtn' id='"+tvpresenter_id+"'>Presenter</button>"+"<button class='ctlbtn' id='"+tvfloor_id+"'>Vid Floor</button>"+"<button class='ctlbtn' id='"+vbanner_id+"'>Banner</button>";if(confMan.canvasCount>1){html+="<br><br><b>Canvas Controls</b><hr noshade>"+"<button class='ctlbtn' id='"+canvas_in_set_id+"'>Set Input Canvas</button>"+"<button class='ctlbtn' id='"+canvas_in_prev_id+"'>Prev Input Canvas</button>"+"<button class='ctlbtn' id='"+canvas_in_next_id+"'>Next Input Canvas</button>"+"<br>"+"<button class='ctlbtn' id='"+canvas_out_set_id+"'>Set Watching Canvas</button>"+"<button class='ctlbtn' id='"+canvas_out_prev_id+"'>Prev Watching Canvas</button>"+"<button class='ctlbtn' id='"+canvas_out_next_id+"'>Next Watching Canvas</button>";} function genControls(jq,rowid){var x=parseInt(rowid);var kick_id="kick_"+x;var canvas_in_next_id="canvas_in_next_"+x;var canvas_in_prev_id="canvas_in_prev_"+x;var canvas_out_next_id="canvas_out_next_"+x;var canvas_out_prev_id="canvas_out_prev_"+x;var canvas_in_set_id="canvas_in_set_"+x;var canvas_out_set_id="canvas_out_set_"+x;var layer_set_id="layer_set_"+x;var layer_next_id="layer_next_"+x;var layer_prev_id="layer_prev_"+x;var tmute_id="tmute_"+x;var tvmute_id="tvmute_"+x;var vbanner_id="vbanner_"+x;var tvpresenter_id="tvpresenter_"+x;var tvfloor_id="tvfloor_"+x;var box_id="box_"+x;var gainup_id="gain_in_up"+x;var gaindn_id="gain_in_dn"+x;var volup_id="vol_in_up"+x;var voldn_id="vol_in_dn"+x;var transfer_id="transfer"+x;var html="<div id='"+box_id+"'>";html+="<b>General Controls</b><hr noshade>";html+="<button class='ctlbtn' id='"+kick_id+"'>Kick</button>"+"<button class='ctlbtn' id='"+tmute_id+"'>Mute</button>"+"<button class='ctlbtn' id='"+gainup_id+"'>Gain -</button>"+"<button class='ctlbtn' id='"+gaindn_id+"'>Gain +</button>"+"<button class='ctlbtn' id='"+voldn_id+"'>Vol -</button>"+"<button class='ctlbtn' id='"+volup_id+"'>Vol +</button>"+"<button class='ctlbtn' id='"+transfer_id+"'>Transfer</button>";if(confMan.params.hasVid){html+="<br><br><b>Video Controls</b><hr noshade>";html+="<button class='ctlbtn' id='"+tvmute_id+"'>VMute</button>"+"<button class='ctlbtn' id='"+tvpresenter_id+"'>Presenter</button>"+"<button class='ctlbtn' id='"+tvfloor_id+"'>Vid Floor</button>"+"<button class='ctlbtn' id='"+vbanner_id+"'>Banner</button>";if(confMan.canvasCount>1){html+="<br><br><b>Canvas Controls</b><hr noshade>"+"<button class='ctlbtn' id='"+canvas_in_set_id+"'>Set Input Canvas</button>"+"<button class='ctlbtn' id='"+canvas_in_prev_id+"'>Prev Input Canvas</button>"+"<button class='ctlbtn' id='"+canvas_in_next_id+"'>Next Input Canvas</button>"+"<br>"+"<button class='ctlbtn' id='"+canvas_out_set_id+"'>Set Watching Canvas</button>"+"<button class='ctlbtn' id='"+canvas_out_prev_id+"'>Prev Watching Canvas</button>"+"<button class='ctlbtn' id='"+canvas_out_next_id+"'>Next Watching Canvas</button>";}
@ -266,7 +266,7 @@ if(dialog.state==state||!checkStateChange(dialog.state,state)){console.error("Di
console.log("Dialog "+dialog.callID+": state change from "+dialog.state.name+" to "+state.name);dialog.lastState=dialog.state;dialog.state=state;if(!dialog.causeCode){dialog.causeCode=16;} console.log("Dialog "+dialog.callID+": state change from "+dialog.state.name+" to "+state.name);dialog.lastState=dialog.state;dialog.state=state;if(!dialog.causeCode){dialog.causeCode=16;}
if(!dialog.cause){dialog.cause="NORMAL CLEARING";} if(!dialog.cause){dialog.cause="NORMAL CLEARING";}
if(dialog.callbacks.onDialogState){dialog.callbacks.onDialogState(this);} if(dialog.callbacks.onDialogState){dialog.callbacks.onDialogState(this);}
switch(dialog.state){case $.verto.enum.state.early:case $.verto.enum.state.active:var speaker=dialog.useSpeak;console.info("Using Speaker: ",speaker);if(speaker&&speaker!=="any"){setTimeout(function(){dialog.setAudioPlaybackDevice(speaker);},500);} switch(dialog.state){case $.verto.enum.state.early:case $.verto.enum.state.active:var speaker=dialog.useSpeak;console.info("Using Speaker: ",speaker);if(speaker&&speaker!=="any"&&speaker!=="none"){setTimeout(function(){dialog.setAudioPlaybackDevice(speaker);},500);}
break;case $.verto.enum.state.trying:setTimeout(function(){if(dialog.state==$.verto.enum.state.trying){dialog.setState($.verto.enum.state.hangup);}},30000);break;case $.verto.enum.state.purge:dialog.setState($.verto.enum.state.destroy);break;case $.verto.enum.state.hangup:if(dialog.lastState.val>$.verto.enum.state.requesting.val&&dialog.lastState.val<$.verto.enum.state.hangup.val){dialog.sendMethod("verto.bye",{});} break;case $.verto.enum.state.trying:setTimeout(function(){if(dialog.state==$.verto.enum.state.trying){dialog.setState($.verto.enum.state.hangup);}},30000);break;case $.verto.enum.state.purge:dialog.setState($.verto.enum.state.destroy);break;case $.verto.enum.state.hangup:if(dialog.lastState.val>$.verto.enum.state.requesting.val&&dialog.lastState.val<$.verto.enum.state.hangup.val){dialog.sendMethod("verto.bye",{});}
dialog.setState($.verto.enum.state.destroy);break;case $.verto.enum.state.destroy:delete dialog.verto.dialogs[dialog.callID];if(dialog.params.screenShare){dialog.rtc.stopPeer();}else{dialog.rtc.stop();} dialog.setState($.verto.enum.state.destroy);break;case $.verto.enum.state.destroy:delete dialog.verto.dialogs[dialog.callID];if(dialog.params.screenShare){dialog.rtc.stopPeer();}else{dialog.rtc.stop();}
break;} break;}

View File

@ -799,6 +799,7 @@ function docall() {
check_vid_res(); check_vid_res();
console.error(outgoingBandwidth, incomingBandwidth); console.error(outgoingBandwidth, incomingBandwidth);
cur_call = vertoHandle.newCall({ cur_call = vertoHandle.newCall({
destination_number: $("#ext").val(), destination_number: $("#ext").val(),
caller_id_name: $("#cidname").val(), caller_id_name: $("#cidname").val(),
@ -807,9 +808,9 @@ function docall() {
incomingBandwidth: incomingBandwidth, incomingBandwidth: incomingBandwidth,
useVideo: check_vid(), useVideo: check_vid(),
useStereo: $("#use_stereo").is(':checked'), useStereo: $("#use_stereo").is(':checked'),
useCamera: sessid ? "none" : $("#usecamera").find(":selected").val(), useCamera: (sessid || canvas_id) ? "none" : $("#usecamera").find(":selected").val(),
useMic: $("#usemic").find(":selected").val(), useMic: (sessid || canvas_id) ? "none" : $("#usemic").find(":selected").val(),
useSpeak: $("#usespeak").find(":selected").val(), useSpeak: (sessid || canvas_id) ? "none" : $("#usespeak").find(":selected").val(),
dedEnc: $("#use_dedenc").is(':checked'), dedEnc: $("#use_dedenc").is(':checked'),
mirrorInput: $("#mirror_input").is(':checked'), mirrorInput: $("#mirror_input").is(':checked'),
userVariables: { userVariables: {

View File

@ -1242,18 +1242,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
pri_hangup(isdn_data->spri.pri, call, caller_data->hangup_cause); pri_hangup(isdn_data->spri.pri, call, caller_data->hangup_cause);
if (chan_priv->peerhangup) { if (chan_priv->peerhangup) {
/* Call is inbound and hangup has been initiated by peer */ ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
if (!ftdm_test_flag(chan, FTDM_CHANNEL_OUTBOUND)) {
ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
} else if (caller_data->hangup_cause == PRI_CAUSE_NO_USER_RESPONSE) {
/* Can happen when we have a DL link expire or some timer expired */
ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
} else if (caller_data->hangup_cause == PRI_CAUSE_DESTINATION_OUT_OF_ORDER) {
/* Can happen when we have a DL link expire or some timer expired */
ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
} else if (caller_data->hangup_cause == PRI_CAUSE_INVALID_NUMBER_FORMAT) {
ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
}
} }
} }
} }

View File

@ -4,7 +4,6 @@
# #
######################################################## ########################################################
# TODO: FreeSWITCH AutoStart # TODO: FreeSWITCH AutoStart
# TODO: Install on Raspbian
# TODO: Allow Selection of Source or Package Install on Debian # TODO: Allow Selection of Source or Package Install on Debian
DIALOG=${DIALOG=dialog} DIALOG=${DIALOG=dialog}
@ -17,7 +16,18 @@ install_prereqs() {
#install the prereqs #install the prereqs
echo "Making sure we have the prereqs for this script to run. Please Stand by..." echo "Making sure we have the prereqs for this script to run. Please Stand by..."
apt-get update 2>&1 >/dev/null apt-get update 2>&1 >/dev/null
apt-get install -y curl dialog git 2>&1 >/dev/null apt-get install -y curl dialog git ntpdate 2>&1 >/dev/null
# See if ntpd is running if it is, stop it set the current time as rpi has no RTC and this is needed
# for SSL to function properly
if pgrep "ntpd" >/dev/null ; then
/etc/init.d/ntp stop
ntpdate pool.ntp.org
/etc/init.d/ntp start
else
ntpdate pool.ntp.org
fi
} }
welcome_screen() { welcome_screen() {
@ -101,7 +111,7 @@ is_private_ip() {
} }
verify_ip_fqdn() { verify_ip_fqdn() {
DNSIP=`dig +noall +answer @4.2.2.2 $FQDN | cut -d' ' -f3` DNSIP=`dig +noall +answer @4.2.2.2 $FQDN | awk '{print $5}'`
dialog --title "NO DNS For this FQDN" --clear \ dialog --title "NO DNS For this FQDN" --clear \
--menu "The FQDN and IP Address do not match what is available in Public DNS Servers." 15 60 5 \ --menu "The FQDN and IP Address do not match what is available in Public DNS Servers." 15 60 5 \
@ -132,13 +142,14 @@ config_fs_repos() {
} }
get_fs_source() { get_fs_source() {
echo "REPO = $REPO"
if [ ! -d /usr/src/freeswitch.git ]; then if [ ! -d /usr/src/freeswitch.git ]; then
cd /usr/src cd /usr/src
git clone $REPO freeswitch.git git clone $REPO freeswitch.git
else else
cd /usr/src/freeswitch.git cd /usr/src/freeswitch.git
git clean -fdx git clean -fdx
git reset -hard origin/$FS_REV git reset --hard origin/$FS_REV
git pull git pull
fi fi
} }
@ -164,7 +175,7 @@ install_certs() {
NEED_CERTS_INSTALL=0 NEED_CERTS_INSTALL=0
else else
echo "Renewing LetsEncrypt Certs as they will expire in the next 30 days." echo "Renewing LetsEncrypt Certs as they will expire in the next 30 days."
./letsencrypt-auto renew ./letsencrypt-auto renew --manual-public-ip-logging-ok
fi fi
else else
echo "Setting up LetsEncrypt and getting you some nice new Certs for this Server." echo "Setting up LetsEncrypt and getting you some nice new Certs for this Server."
@ -192,7 +203,9 @@ build_fs() {
rm -rf /usr/local/freeswitch/{bin,mod,lib}/* rm -rf /usr/local/freeswitch/{bin,mod,lib}/*
fi fi
cd /usr/src/freeswitch.git cd /usr/src/freeswitch.git
./bootstrap.sh -j if [ ! -d /usr/src/freeswitch.git/configure ]; then
./bootstrap.sh -j
fi
./configure -C ./configure -C
make -j$JLIMIT install make -j$JLIMIT install
make uhd-sounds-install make uhd-sounds-install
@ -238,6 +251,7 @@ freeswitch_raspbian_source() {
libtiff5-dev libperl-dev libgdbm-dev libdb-dev gettext libssl-dev libcurl4-openssl-dev libpcre3-dev libspeex-dev \ libtiff5-dev libperl-dev libgdbm-dev libdb-dev gettext libssl-dev libcurl4-openssl-dev libpcre3-dev libspeex-dev \
libspeexdsp-dev libsqlite3-dev libedit-dev libldns-dev libpq-dev libsndfile-dev libopus-dev liblua5.1-0-dev 2>&1 | \ libspeexdsp-dev libsqlite3-dev libedit-dev libldns-dev libpq-dev libsndfile-dev libopus-dev liblua5.1-0-dev 2>&1 | \
awk -W interactive '/Progress/ { print }'| sed -u 's/[^0-9]//g' | dialog --gauge "Please wait.\n Installing Build Requirements..." 10 70 0 awk -W interactive '/Progress/ { print }'| sed -u 's/[^0-9]//g' | dialog --gauge "Please wait.\n Installing Build Requirements..." 10 70 0
build_fs
} }
@ -247,11 +261,12 @@ fs_ver_select
get_network_settings get_network_settings
if [ "$ID" = "debian" ]; then if [ "$ID" = "debian" ]; then
## These only work on Jessie at this time
config_fs_repos config_fs_repos
freeswitch_debian_source freeswitch_debian_source
elif [ "$ID" = "raspbian" ]; then elif [ "$ID" = "raspbian" ]; then
#freeswitch_raspbiani123
JLIMIT="3" JLIMIT="3"
freeswitch_raspbian_source
fi fi
install_vc install_vc
@ -264,10 +279,10 @@ if [ "x$PRIVIP" != "x$IPADDR" ]; then
elif [ $VIPFQDN -eq 1 ]; then elif [ $VIPFQDN -eq 1 ]; then
echo "Skipping LetsEncrypt\n" echo "Skipping LetsEncrypt\n"
else else
get_dletsencrypt get_letsencrypt
install_certs install_certs
fi fi
else else
echo "Skipping LetsEncrypt. Since we are on Private IP Space"; echo "Skipping LetsEncrypt. Since we are on a Private IP Address";
fi fi

View File

@ -42,6 +42,7 @@ SWITCH_BEGIN_EXTERN_C
#define SWITCH_NO_CRYPTO_TAG -1 #define SWITCH_NO_CRYPTO_TAG -1
typedef enum { typedef enum {
DTMF_AUTO,
DTMF_2833, DTMF_2833,
DTMF_INFO, DTMF_INFO,
DTMF_NONE DTMF_NONE

View File

@ -368,6 +368,19 @@ SWITCH_DECLARE(switch_image_t *) switch_img_write_text_img(int w, int h, switch_
SWITCH_DECLARE(switch_image_t *) switch_img_read_file(const char* file_name); SWITCH_DECLARE(switch_image_t *) switch_img_read_file(const char* file_name);
SWITCH_DECLARE(switch_status_t) switch_img_letterbox(switch_image_t *img, switch_image_t **imgP, int width, int height, const char *color); SWITCH_DECLARE(switch_status_t) switch_img_letterbox(switch_image_t *img, switch_image_t **imgP, int width, int height, const char *color);
SWITCH_DECLARE(switch_bool_t) switch_core_has_video(void); SWITCH_DECLARE(switch_bool_t) switch_core_has_video(void);
/*!\brief I420 to I420 Copy*/
SWITCH_DECLARE(switch_status_t) switch_I420_copy(const uint8_t* src_y, int src_stride_y,
const uint8_t* src_u, int src_stride_u,
const uint8_t* src_v, int src_stride_v,
uint8_t* dst_y, int dst_stride_y,
uint8_t* dst_u, int dst_stride_u,
uint8_t* dst_v, int dst_stride_v,
int width, int height);
SWITCH_DECLARE(switch_status_t) switch_I420_copy2(uint8_t *src_planes[], int src_stride[],
uint8_t *dst_planes[], int dst_stride[],
int width, int height);
/** @} */ /** @} */
SWITCH_END_EXTERN_C SWITCH_END_EXTERN_C

View File

@ -600,6 +600,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_3p_media(const char *uuid, switch_med
SWITCH_DECLARE(switch_status_t) switch_ivr_nomedia(const char *uuid, switch_media_flag_t flags); SWITCH_DECLARE(switch_status_t) switch_ivr_nomedia(const char *uuid, switch_media_flag_t flags);
SWITCH_DECLARE(switch_status_t) switch_ivr_3p_nomedia(const char *uuid, switch_media_flag_t flags); SWITCH_DECLARE(switch_status_t) switch_ivr_3p_nomedia(const char *uuid, switch_media_flag_t flags);
SWITCH_DECLARE(void) switch_ivr_bg_media(const char *uuid, switch_media_flag_t flags, switch_bool_t on, switch_bool_t is3p, uint32_t delay);
/*! /*!
\brief Signal the session with a protocol specific hold message. \brief Signal the session with a protocol specific hold message.
\param uuid the uuid of the session to hold \param uuid the uuid of the session to hold

View File

@ -326,6 +326,7 @@ typedef struct switch_mm_s {
int vbuf; int vbuf;
switch_video_profile_t vprofile; switch_video_profile_t vprofile;
switch_video_encode_speed_t vencspd; switch_video_encode_speed_t vencspd;
uint8_t try_hardware_encoder;
} switch_mm_t; } switch_mm_t;
/*! an abstract representation of a file handle (some parameters based on compat with libsndfile) */ /*! an abstract representation of a file handle (some parameters based on compat with libsndfile) */
@ -393,6 +394,10 @@ struct switch_file_handle {
char *modname; char *modname;
switch_mm_t mm; switch_mm_t mm;
switch_mutex_t *flag_mutex; switch_mutex_t *flag_mutex;
/*! total video duration, or total page in pdf*/
int64_t duration;
/*! current video position, or current page in pdf */
int64_t vpos;
}; };
/*! \brief Abstract interface to an asr module */ /*! \brief Abstract interface to an asr module */
@ -633,6 +638,7 @@ struct switch_video_codec_settings {
uint32_t bandwidth; uint32_t bandwidth;
int32_t width; int32_t width;
int32_t height; int32_t height;
uint8_t try_hardware_encoder;
}; };
union switch_codec_settings { union switch_codec_settings {

View File

@ -2551,7 +2551,8 @@ typedef enum {
SWITCH_MEDIA_FLOW_SENDRECV = 0, SWITCH_MEDIA_FLOW_SENDRECV = 0,
SWITCH_MEDIA_FLOW_SENDONLY, SWITCH_MEDIA_FLOW_SENDONLY,
SWITCH_MEDIA_FLOW_RECVONLY, SWITCH_MEDIA_FLOW_RECVONLY,
SWITCH_MEDIA_FLOW_INACTIVE SWITCH_MEDIA_FLOW_INACTIVE,
SWITCH_MEDIA_FLOW_DISABLED
} switch_media_flow_t; } switch_media_flow_t;
typedef enum { typedef enum {
@ -2599,7 +2600,8 @@ typedef enum {
} switch_vid_spy_fmt_t; } switch_vid_spy_fmt_t;
typedef enum { typedef enum {
SCFC_FLUSH_AUDIO SCFC_FLUSH_AUDIO,
SCFC_PAUSE_READ
} switch_file_command_t; } switch_file_command_t;
SWITCH_END_EXTERN_C SWITCH_END_EXTERN_C

View File

@ -27,7 +27,7 @@
* Anthony Minessale <anthm@freeswitch.org> * Anthony Minessale <anthm@freeswitch.org>
* Emmanuel Schmidbauer <eschmidbauer@gmail.com> * Emmanuel Schmidbauer <eschmidbauer@gmail.com>
* *
* mod_avcodec -- Codec with libav.org * mod_avcodec -- Codec with libav.org and ffmpeg
* *
*/ */
@ -91,7 +91,11 @@ static const uint8_t *fs_avc_find_startcode_internal(const uint8_t *p, const uin
const uint8_t *fs_avc_find_startcode(const uint8_t *p, const uint8_t *end){ const uint8_t *fs_avc_find_startcode(const uint8_t *p, const uint8_t *end){
const uint8_t *out= fs_avc_find_startcode_internal(p, end); const uint8_t *out= fs_avc_find_startcode_internal(p, end);
if(p<out && out<end && !out[-1]) out--;
if (p < out && out < end && !out[-1]) {
out--;
}
return out; return out;
} }
@ -150,8 +154,7 @@ typedef struct h263_state_s {
int quant; int quant;
} h263_state_t; } h263_state_t;
typedef struct our_h264_nalu_s typedef struct our_h264_nalu_s {
{
const uint8_t *start; const uint8_t *start;
const uint8_t *eat; const uint8_t *eat;
uint32_t len; uint32_t len;
@ -181,9 +184,11 @@ typedef struct h264_codec_context_s {
AVCodecContext *encoder_ctx; AVCodecContext *encoder_ctx;
AVFrame *encoder_avframe; AVFrame *encoder_avframe;
AVPacket encoder_avpacket; AVPacket encoder_avpacket;
AVFrame *decoder_avframe;
our_h264_nalu_t nalus[MAX_NALUS]; our_h264_nalu_t nalus[MAX_NALUS];
enum AVCodecID av_codec_id; enum AVCodecID av_codec_id;
uint16_t last_seq; // last received frame->seq uint16_t last_seq; // last received frame->seq
int hw_encoder;
} h264_codec_context_t; } h264_codec_context_t;
static uint8_t ff_input_buffer_padding[FF_INPUT_BUFFER_PADDING_SIZE] = { 0 }; static uint8_t ff_input_buffer_padding[FF_INPUT_BUFFER_PADDING_SIZE] = { 0 };
@ -420,9 +425,12 @@ static switch_status_t buffer_h263_rfc4629_packets(h264_codec_context_t *context
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
#ifndef H263_MODE_B
/* this function is depracated from ffmpeg 3.0 and
https://lists.libav.org/pipermail/libav-devel/2015-October/072782.html
*/
void rtp_callback(struct AVCodecContext *avctx, void *data, int size, int mb_nb) void rtp_callback(struct AVCodecContext *avctx, void *data, int size, int mb_nb)
{ {
#ifndef H263_MODE_B
uint8_t *d = data; uint8_t *d = data;
uint32_t code = (ntohl(*(uint32_t *)data) & 0xFFFFFC00) >> 10; uint32_t code = (ntohl(*(uint32_t *)data) & 0xFFFFFC00) >> 10;
h264_codec_context_t *context = (h264_codec_context_t *)avctx->opaque; h264_codec_context_t *context = (h264_codec_context_t *)avctx->opaque;
@ -449,8 +457,8 @@ void rtp_callback(struct AVCodecContext *avctx, void *data, int size, int mb_nb)
size > 1500 ? "===============Exceedding MTU===============" : ""); size > 1500 ? "===============Exceedding MTU===============" : "");
#endif #endif
#endif
} }
#endif
const uint8_t *fs_h263_find_resync_marker_reverse(const uint8_t *restrict start, const uint8_t *fs_h263_find_resync_marker_reverse(const uint8_t *restrict start,
const uint8_t *restrict end) const uint8_t *restrict end)
@ -728,81 +736,103 @@ static switch_status_t consume_h263p_bitstream(h264_codec_context_t *context, sw
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "len: %d, mark:%d %02x %02x %02x %02x\n", frame->datalen, frame->m, *p, *(p+1), *(p+2), *(p+3)); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "len: %d, mark:%d %02x %02x %02x %02x\n", frame->datalen, frame->m, *p, *(p+1), *(p+2), *(p+3));
} }
if (frame->m) av_free_packet(&context->encoder_avpacket); if (frame->m) {
av_packet_unref(&context->encoder_avpacket);
return SWITCH_STATUS_SUCCESS;
}
return frame->m ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_MORE_DATA; return SWITCH_STATUS_MORE_DATA;
}
static switch_status_t consume_h264_bitstream(h264_codec_context_t *context, switch_frame_t *frame)
{
AVPacket *pkt = &context->encoder_avpacket;
our_h264_nalu_t *nalu = &context->nalus[context->nalu_current_index];
uint8_t nalu_hdr = *(uint8_t *)(nalu->start);
uint8_t nalu_type = nalu_hdr & 0x1f;
uint8_t nri = nalu_hdr & 0x60;
int left = nalu->len - (nalu->eat - nalu->start);
uint8_t *p = frame->data;
uint8_t start = nalu->start == nalu->eat ? 0x80 : 0;
if (nalu->len <= SLICE_SIZE) {
memcpy(frame->data, nalu->start, nalu->len);
frame->datalen = nalu->len;
context->nalu_current_index++;
if (nalu_type == 6 || nalu_type == 7 || nalu_type == 8 || context->nalus[context->nalu_current_index].len) {
frame->m = 0;
return SWITCH_STATUS_MORE_DATA;
}
if (pkt->size > 0) av_packet_unref(pkt);
return SWITCH_STATUS_SUCCESS;
}
if (left <= (SLICE_SIZE - 2)) {
p[0] = nri | 28; // FU-A
p[1] = 0x40 | nalu_type;
memcpy(p+2, nalu->eat, left);
nalu->eat += left;
frame->datalen = left + 2;
frame->m = 1;
context->nalu_current_index++;
if (pkt->size > 0) av_packet_unref(pkt);
return SWITCH_STATUS_SUCCESS;
}
p[0] = nri | 28; // FU-A
p[1] = start | nalu_type;
if (start) nalu->eat++;
memcpy(p+2, nalu->eat, SLICE_SIZE - 2);
nalu->eat += (SLICE_SIZE - 2);
frame->datalen = SLICE_SIZE;
return SWITCH_STATUS_MORE_DATA;
} }
static switch_status_t consume_nalu(h264_codec_context_t *context, switch_frame_t *frame) static switch_status_t consume_nalu(h264_codec_context_t *context, switch_frame_t *frame)
{ {
AVPacket *pkt = &context->encoder_avpacket;
our_h264_nalu_t *nalu = &context->nalus[context->nalu_current_index]; our_h264_nalu_t *nalu = &context->nalus[context->nalu_current_index];
if (!nalu->len) { if (!nalu->len) {
frame->datalen = 0; frame->datalen = 0;
frame->m = 0; frame->m = 0;
if (context->encoder_avpacket.size > 0) av_free_packet(&context->encoder_avpacket); if (pkt->size > 0) av_packet_unref(pkt);
// if (context->encoder_avframe->data[0]) av_freep(&context->encoder_avframe->data[0]);
context->nalu_current_index = 0; context->nalu_current_index = 0;
return SWITCH_STATUS_NOTFOUND; return SWITCH_STATUS_NOTFOUND;
} }
if (context->av_codec_id == AV_CODEC_ID_H263) { if (context->av_codec_id == AV_CODEC_ID_H263) {
return consume_h263_bitstream(context, frame); return consume_h263_bitstream(context, frame);
} else if (context->av_codec_id == AV_CODEC_ID_H263P) { }
if (context->av_codec_id == AV_CODEC_ID_H263P) {
return consume_h263p_bitstream(context, frame); return consume_h263p_bitstream(context, frame);
} }
assert(nalu->len); return consume_h264_bitstream(context, frame);
if (nalu->len <= SLICE_SIZE) {
uint8_t nalu_hdr = *(uint8_t *)(nalu->start);
uint8_t nalu_type = nalu_hdr & 0x1f;
memcpy(frame->data, nalu->start, nalu->len);
frame->datalen = nalu->len;
context->nalu_current_index++;
if (nalu_type == 6 || nalu_type == 7 || nalu_type == 8) {
frame->m = 0;
return SWITCH_STATUS_MORE_DATA;
}
frame->m = context->nalus[context->nalu_current_index].len ? 0 : 1;
return frame->m ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_MORE_DATA;
} else {
uint8_t nalu_hdr = *(uint8_t *)(nalu->start);
uint8_t nri = nalu_hdr & 0x60;
uint8_t nalu_type = nalu_hdr & 0x1f;
int left = nalu->len - (nalu->eat - nalu->start);
uint8_t *p = frame->data;
if (left <= (SLICE_SIZE - 2)) {
p[0] = nri | 28; // FU-A
p[1] = 0x40 | nalu_type;
memcpy(p+2, nalu->eat, left);
nalu->eat += left;
frame->datalen = left + 2;
frame->m = 1;
context->nalu_current_index++;
return SWITCH_STATUS_SUCCESS;
} else {
uint8_t start = nalu->start == nalu->eat ? 0x80 : 0;
p[0] = nri | 28; // FU-A
p[1] = start | nalu_type;
if (start) nalu->eat++;
memcpy(p+2, nalu->eat, SLICE_SIZE - 2);
nalu->eat += (SLICE_SIZE - 2);
frame->datalen = SLICE_SIZE;
return SWITCH_STATUS_MORE_DATA;
}
}
} }
static switch_status_t open_encoder(h264_codec_context_t *context, uint32_t width, uint32_t height) static switch_status_t open_encoder(h264_codec_context_t *context, uint32_t width, uint32_t height)
{ {
int sane = 0; int sane = 0;
if (!context->encoder) context->encoder = avcodec_find_encoder(context->av_codec_id); if (!context->encoder) {
if (context->av_codec_id == AV_CODEC_ID_H264) {
if (context->codec_settings.video.try_hardware_encoder && (context->encoder = avcodec_find_encoder_by_name("nvenc_h264"))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "NVENC HW CODEC ENABLED\n");
context->hw_encoder = 1;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "NVENC HW CODEC NOT PRESENT\n");
}
}
if (!context->encoder) {
context->encoder = avcodec_find_encoder(context->av_codec_id);
}
}
if (!context->encoder) { if (!context->encoder) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find encoder id: %d\n", context->av_codec_id); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find encoder id: %d\n", context->av_codec_id);
@ -868,50 +898,60 @@ static switch_status_t open_encoder(h264_codec_context_t *context, uint32_t widt
context->encoder_ctx->bit_rate = context->bandwidth * 1024; context->encoder_ctx->bit_rate = context->bandwidth * 1024;
context->encoder_ctx->rc_max_rate = context->bandwidth * 1024; context->encoder_ctx->rc_max_rate = context->bandwidth * 1024;
context->encoder_ctx->rc_buffer_size = context->bandwidth * 1024 * 4; context->encoder_ctx->rc_buffer_size = context->bandwidth * 1024 * 4;
context->encoder_ctx->rtp_payload_size = SLICE_SIZE;
if (context->av_codec_id == AV_CODEC_ID_H263 || context->av_codec_id == AV_CODEC_ID_H263P) { if (context->av_codec_id == AV_CODEC_ID_H263 || context->av_codec_id == AV_CODEC_ID_H263P) {
#ifndef H263_MODE_B
# if defined(__ICL) || defined (__INTEL_COMPILER)
# define FF_DISABLE_DEPRECATION_WARNINGS __pragma(warning(push)) __pragma(warning(disable:1478))
# define FF_ENABLE_DEPRECATION_WARNINGS __pragma(warning(pop))
# elif defined(_MSC_VER)
# define FF_DISABLE_DEPRECATION_WARNINGS __pragma(warning(push)) __pragma(warning(disable:4996))
# define FF_ENABLE_DEPRECATION_WARNINGS __pragma(warning(pop))
# else
# define FF_DISABLE_DEPRECATION_WARNINGS _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
# define FF_ENABLE_DEPRECATION_WARNINGS _Pragma("GCC diagnostic warning \"-Wdeprecated-declarations\"")
# endif
FF_DISABLE_DEPRECATION_WARNINGS
context->encoder_ctx->rtp_callback = rtp_callback; context->encoder_ctx->rtp_callback = rtp_callback;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
context->encoder_ctx->rc_min_rate = context->encoder_ctx->rc_max_rate; context->encoder_ctx->rc_min_rate = context->encoder_ctx->rc_max_rate;
context->encoder_ctx->opaque = context; context->encoder_ctx->opaque = context;
av_opt_set_int(context->encoder_ctx->priv_data, "mb_info", SLICE_SIZE - 8, 0); av_opt_set_int(context->encoder_ctx->priv_data, "mb_info", SLICE_SIZE - 8, 0);
} else if (context->av_codec_id == AV_CODEC_ID_H264) { } else if (context->av_codec_id == AV_CODEC_ID_H264) {
context->encoder_ctx->profile = FF_PROFILE_H264_BASELINE; context->encoder_ctx->profile = FF_PROFILE_H264_BASELINE;
context->encoder_ctx->level = 41; context->encoder_ctx->level = 41;
av_opt_set(context->encoder_ctx->priv_data, "preset", "veryfast", 0);
av_opt_set(context->encoder_ctx->priv_data, "tune", "zerolatency", 0);
av_opt_set(context->encoder_ctx->priv_data, "profile", "baseline", 0);
//av_opt_set_int(context->encoder_ctx->priv_data, "slice-max-size", SLICE_SIZE, 0);
// libx264-medium.ffpreset preset if (context->hw_encoder) {
av_opt_set(context->encoder_ctx->priv_data, "preset", "llhp", 0);
av_opt_set_int(context->encoder_ctx->priv_data, "2pass", 1, 0);
} else {
av_opt_set(context->encoder_ctx->priv_data, "preset", "veryfast", 0);
av_opt_set(context->encoder_ctx->priv_data, "tune", "zerolatency", 0);
av_opt_set(context->encoder_ctx->priv_data, "profile", "baseline", 0);
av_opt_set_int(context->encoder_ctx->priv_data, "slice-max-size", SLICE_SIZE, 0);
av_opt_set_int(context->encoder_ctx->priv_data, "sc_threshold", 40, 0);
av_opt_set_int(context->encoder_ctx->priv_data, "b_strategy", 1, 0);
av_opt_set_int(context->encoder_ctx->priv_data, "crf", 18, 0);
context->encoder_ctx->coder_type = 1; // coder = 1 // libx264-medium.ffpreset preset
context->encoder_ctx->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop
context->encoder_ctx->me_cmp|= 1; // cmp=+chroma, where CHROMA = 1
context->encoder_ctx->me_method=ME_HEX; // me_method=hex
//context->encoder_ctx->me_subpel_quality = 7; // subq=7
context->encoder_ctx->me_range = 16; // me_range=16 context->encoder_ctx->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop
context->encoder_ctx->max_b_frames = 3; // bf=3 context->encoder_ctx->me_cmp|= 1; // cmp=+chroma, where CHROMA = 1
context->encoder_ctx->me_range = 21; // me_range=16
//context->encoder_ctx->refs = 3; // refs=3 context->encoder_ctx->max_b_frames = 3; // bf=3
//context->encoder_ctx->refs = 3; // refs=3
//context->encoder_ctx->trellis = 1; // trellis=1 context->encoder_ctx->gop_size = 250; // g=250
context->encoder_ctx->keyint_min = 25; // keyint_min=25
context->encoder_ctx->i_quant_factor = 0.71; // i_qfactor=0.71
context->encoder_ctx->b_quant_factor = 0.76923078; // Qscale difference between P-frames and B-frames.
context->encoder_ctx->qcompress = 0.6; // qcomp=0.6
context->encoder_ctx->qmin = 10; // qmin=10
context->encoder_ctx->qmax = 51; // qmax=51
context->encoder_ctx->max_qdiff = 4; // qdiff=4
}
} }
// libx264-medium.ffpreset preset
context->encoder_ctx->gop_size = 250; // g=250
context->encoder_ctx->keyint_min = 25; // keyint_min=25
context->encoder_ctx->scenechange_threshold = 40; // sc_threshold=40
context->encoder_ctx->i_quant_factor = 0.71; // i_qfactor=0.71
context->encoder_ctx->b_frame_strategy = 1; // b_strategy=1
context->encoder_ctx->qcompress = 0.6; // qcomp=0.6
context->encoder_ctx->qmin = 10; // qmin=10
context->encoder_ctx->qmax = 51; // qmax=51
context->encoder_ctx->max_qdiff = 4; // qdiff=4
if (avcodec_open2(context->encoder_ctx, context->encoder, NULL) < 0) { if (avcodec_open2(context->encoder_ctx, context->encoder, NULL) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open codec\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open codec\n");
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
@ -923,66 +963,62 @@ static switch_status_t open_encoder(h264_codec_context_t *context, uint32_t widt
static switch_status_t switch_h264_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) static switch_status_t switch_h264_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings)
{ {
int encoding, decoding; int encoding, decoding;
h264_codec_context_t *context = NULL;
encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
decoding = (flags & SWITCH_CODEC_FLAG_DECODE); decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
if (!(encoding || decoding)) { if (!(encoding || decoding)) {
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
} else {
h264_codec_context_t *context = NULL;
if (codec->fmtp_in) {
codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in);
}
context = switch_core_alloc(codec->memory_pool, sizeof(h264_codec_context_t));
switch_assert(context);
memset(context, 0, sizeof(*context));
if (codec_settings) {
context->codec_settings = *codec_settings;
}
if (!strcmp(codec->implementation->iananame, "H263")) {
context->av_codec_id = AV_CODEC_ID_H263;
} else if (!strcmp(codec->implementation->iananame, "H263-1998")) {
context->av_codec_id = AV_CODEC_ID_H263P;
} else {
context->av_codec_id = AV_CODEC_ID_H264;
}
if (decoding) {
context->decoder = avcodec_find_decoder(context->av_codec_id);
if (!context->decoder && context->av_codec_id == AV_CODEC_ID_H263P) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Cannot find AV_CODEC_ID_H263P decoder, trying AV_CODEC_ID_H263 instead\n");
context->decoder = avcodec_find_decoder(AV_CODEC_ID_H263);
}
if (!context->decoder) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find codec id %d\n", context->av_codec_id);
goto error;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "codec: id=%d %s\n", context->decoder->id, context->decoder->long_name);
context->decoder_ctx = avcodec_alloc_context3(context->decoder);
if (avcodec_open2(context->decoder_ctx, context->decoder, NULL) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error openning codec\n");
goto error;
}
}
if (encoding) {
// never mind
}
switch_buffer_create_dynamic(&(context->nalu_buffer), H264_NALU_BUFFER_SIZE, H264_NALU_BUFFER_SIZE * 8, 0);
codec->private_info = context;
return SWITCH_STATUS_SUCCESS;
} }
if (codec->fmtp_in) {
codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in);
}
context = switch_core_alloc(codec->memory_pool, sizeof(h264_codec_context_t));
switch_assert(context);
memset(context, 0, sizeof(*context));
if (codec_settings) {
context->codec_settings = *codec_settings;
}
if (!strcmp(codec->implementation->iananame, "H263")) {
context->av_codec_id = AV_CODEC_ID_H263;
} else if (!strcmp(codec->implementation->iananame, "H263-1998")) {
context->av_codec_id = AV_CODEC_ID_H263P;
} else {
context->av_codec_id = AV_CODEC_ID_H264;
}
if (decoding) {
context->decoder = avcodec_find_decoder(context->av_codec_id);
if (!context->decoder && context->av_codec_id == AV_CODEC_ID_H263P) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Cannot find AV_CODEC_ID_H263P decoder, trying AV_CODEC_ID_H263 instead\n");
context->decoder = avcodec_find_decoder(AV_CODEC_ID_H263);
}
if (!context->decoder) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find codec id %d\n", context->av_codec_id);
goto error;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "codec: id=%d %s\n", context->decoder->id, context->decoder->long_name);
context->decoder_ctx = avcodec_alloc_context3(context->decoder);
if (avcodec_open2(context->decoder_ctx, context->decoder, NULL) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error openning codec\n");
goto error;
}
}
switch_buffer_create_dynamic(&(context->nalu_buffer), H264_NALU_BUFFER_SIZE, H264_NALU_BUFFER_SIZE * 8, 0);
codec->private_info = context;
return SWITCH_STATUS_SUCCESS;
error: error:
// todo, do some clean up // todo, do some clean up
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
@ -990,22 +1026,9 @@ error:
static void __attribute__((unused)) fill_avframe(AVFrame *pict, switch_image_t *img) static void __attribute__((unused)) fill_avframe(AVFrame *pict, switch_image_t *img)
{ {
int i; switch_I420_copy2(img->planes, img->stride,
uint8_t *y = img->planes[0]; pict->data, pict->linesize,
uint8_t *u = img->planes[1]; img->d_w, img->d_h);
uint8_t *v = img->planes[2];
/* Y */
for (i = 0; i < pict->height; i++) {
memcpy(&pict->data[0][i * pict->linesize[0]], y + i * img->stride[0], pict->width);
}
/* U/V */
for(i = 0; i < pict->height / 2; i++) {
memcpy(&pict->data[1][i * pict->linesize[1]], u + i * img->stride[1], pict->width / 2);
memcpy(&pict->data[2][i * pict->linesize[2]], v + i * img->stride[2], pict->width / 2);
}
} }
static switch_status_t switch_h264_encode(switch_codec_t *codec, switch_frame_t *frame) static switch_status_t switch_h264_encode(switch_codec_t *codec, switch_frame_t *frame)
@ -1029,7 +1052,8 @@ static switch_status_t switch_h264_encode(switch_codec_t *codec, switch_frame_t
height = img->d_h; height = img->d_h;
if (context->av_codec_id == AV_CODEC_ID_H263 && (!is_valid_h263_dimension(width, height))) { if (context->av_codec_id == AV_CODEC_ID_H263 && (!is_valid_h263_dimension(width, height))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "You want %dx%d, but valid H263 sizes are 128x96, 176x144, 352x288, 704x576, and 1408x1152. Try H.263+\n", width, height); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"You want %dx%d, but valid H263 sizes are 128x96, 176x144, 352x288, 704x576, and 1408x1152. Try H.263+\n", width, height);
goto error; goto error;
} }
@ -1149,7 +1173,10 @@ static switch_status_t switch_h264_encode(switch_codec_t *codec, switch_frame_t
*got_output = 0; *got_output = 0;
if (context->av_codec_id == AV_CODEC_ID_H263) { if (context->av_codec_id == AV_CODEC_ID_H263) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG5, "Encoded frame %" SWITCH_INT64_T_FMT " (size=%5d) [0x%02x 0x%02x 0x%02x 0x%02x] got_output: %d slices: %d\n", context->pts, pkt->size, *((uint8_t *)pkt->data), *((uint8_t *)(pkt->data + 1)), *((uint8_t *)(pkt->data + 2)), *((uint8_t *)(pkt->data + 3)), *got_output, avctx->slices); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG5,
"Encoded frame %" SWITCH_INT64_T_FMT " (size=%5d) [0x%02x 0x%02x 0x%02x 0x%02x] got_output: %d slices: %d\n",
context->pts, pkt->size, *((uint8_t *)pkt->data), *((uint8_t *)(pkt->data + 1)), *((uint8_t *)(pkt->data + 2)),
*((uint8_t *)(pkt->data + 3)), *got_output, avctx->slices);
#ifdef H263_MODE_B #ifdef H263_MODE_B
fs_rtp_parse_h263_rfc2190(context, pkt); fs_rtp_parse_h263_rfc2190(context, pkt);
@ -1158,12 +1185,17 @@ static switch_status_t switch_h264_encode(switch_codec_t *codec, switch_frame_t
context->nalu_current_index = 0; context->nalu_current_index = 0;
return consume_nalu(context, frame); return consume_nalu(context, frame);
} else if (context->av_codec_id == AV_CODEC_ID_H263P){ } else if (context->av_codec_id == AV_CODEC_ID_H263P){
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG5, "Encoded frame %" SWITCH_INT64_T_FMT " (size=%5d) [0x%02x 0x%02x 0x%02x 0x%02x] got_output: %d slices: %d\n", context->pts, pkt->size, *((uint8_t *)pkt->data), *((uint8_t *)(pkt->data + 1)), *((uint8_t *)(pkt->data + 2)), *((uint8_t *)(pkt->data + 3)), *got_output, avctx->slices); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG5,
"Encoded frame %" SWITCH_INT64_T_FMT " (size=%5d) [0x%02x 0x%02x 0x%02x 0x%02x] got_output: %d slices: %d\n",
context->pts, pkt->size, *((uint8_t *)pkt->data), *((uint8_t *)(pkt->data + 1)), *((uint8_t *)(pkt->data + 2)),
*((uint8_t *)(pkt->data + 3)), *got_output, avctx->slices);
fs_rtp_parse_h263_rfc4629(context, pkt); fs_rtp_parse_h263_rfc4629(context, pkt);
context->nalu_current_index = 0; context->nalu_current_index = 0;
return consume_nalu(context, frame); return consume_nalu(context, frame);
} else { } else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG5, "Encoded frame %" SWITCH_INT64_T_FMT " (size=%5d) nalu_type=0x%x %d\n", context->pts, pkt->size, *((uint8_t *)pkt->data +4), *got_output); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG5,
"Encoded frame %" SWITCH_INT64_T_FMT " (size=%5d) nalu_type=0x%x %d\n",
context->pts, pkt->size, *((uint8_t *)pkt->data +4), *got_output);
} }
/* split into nalus */ /* split into nalus */
memset(context->nalus, 0, sizeof(context->nalus)); memset(context->nalus, 0, sizeof(context->nalus));
@ -1237,57 +1269,40 @@ static switch_status_t switch_h264_decode(switch_codec_t *codec, switch_frame_t
if (size > 0) { if (size > 0) {
av_init_packet(&pkt); av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
switch_buffer_write(context->nalu_buffer, ff_input_buffer_padding, sizeof(ff_input_buffer_padding)); switch_buffer_write(context->nalu_buffer, ff_input_buffer_padding, sizeof(ff_input_buffer_padding));
switch_buffer_peek_zerocopy(context->nalu_buffer, (const void **)&pkt.data); switch_buffer_peek_zerocopy(context->nalu_buffer, (const void **)&pkt.data);
pkt.size = size; pkt.size = size;
picture = av_frame_alloc();
assert(picture); if (!context->decoder_avframe) context->decoder_avframe = av_frame_alloc();
picture = context->decoder_avframe;
switch_assert(picture);
decoded_len = avcodec_decode_video2(avctx, picture, &got_picture, &pkt); decoded_len = avcodec_decode_video2(avctx, picture, &got_picture, &pkt);
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "buffer: %d got pic: %d len: %d [%dx%d]\n", size, got_picture, decoded_len, avctx->width, avctx->height); // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "buffer: %d got pic: %d len: %d [%dx%d]\n", size, got_picture, decoded_len, picture->width, picture->height);
if (got_picture && decoded_len > 0) { if (got_picture && decoded_len > 0) {
int width = avctx->width; int width = picture->width;
int height = avctx->height; int height = picture->height;
int i;
if (!context->img || (context->img->d_w != width || context->img->d_h != height)) { if (!context->img || (context->img->d_w != width || context->img->d_h != height)) {
//context->img = switch_img_wrap(NULL, SWITCH_IMG_FMT_I420, width, height, 0, picture->data[0]); switch_img_free(&context->img);
context->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, width, height, 1); context->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, width, height, 1);
assert(context->img); switch_assert(context->img);
} }
#if 0
context->img->w = picture->linesize[0]; context->img->w = picture->linesize[0];
context->img->h = picture->linesize[1]; context->img->h = picture->linesize[1];
context->img->d_w = width; context->img->d_w = width;
context->img->d_h = height; context->img->d_h = height;
#endif
//context->img->planes[0] = picture->data[0]; switch_I420_copy2(picture->data, picture->linesize,
//context->img->planes[1] = picture->data[1]; context->img->planes, context->img->stride,
//context->img->planes[2] = picture->data[2]; width, height);
//context->img->stride[0] = picture->linesize[0];
//context->img->stride[1] = picture->linesize[1];
//context->img->stride[2] = picture->linesize[2];
for (i = 0; i < height; i++) {
memcpy(context->img->planes[SWITCH_PLANE_Y] + context->img->stride[SWITCH_PLANE_Y] * i,
picture->data[SWITCH_PLANE_Y] + picture->linesize[SWITCH_PLANE_Y] * i, width);
}
for (i = 0; i < height / 2; i++) {
memcpy(context->img->planes[SWITCH_PLANE_U] + context->img->stride[SWITCH_PLANE_U] * i,
picture->data[SWITCH_PLANE_U] + picture->linesize[SWITCH_PLANE_U] * i, width / 2);
memcpy(context->img->planes[SWITCH_PLANE_V] + context->img->stride[SWITCH_PLANE_V] * i,
picture->data[SWITCH_PLANE_V] + picture->linesize[SWITCH_PLANE_V] * i, width / 2);
}
frame->img = context->img; frame->img = context->img;
} }
av_frame_free(&picture); av_frame_unref(picture);
av_free_packet(&pkt);
} }
switch_buffer_zero(context->nalu_buffer); switch_buffer_zero(context->nalu_buffer);
@ -1366,6 +1381,10 @@ static switch_status_t switch_h264_destroy(switch_codec_t *codec)
av_frame_free(&context->encoder_avframe); av_frame_free(&context->encoder_avframe);
} }
if (context->decoder_avframe) {
av_frame_free(&context->decoder_avframe);
}
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
@ -1416,8 +1435,9 @@ static unsigned get_codecs_sorted(const AVCodecDescriptor ***rcodecs)
return 0; return 0;
} }
desc = NULL; desc = NULL;
while ((desc = avcodec_descriptor_next(desc))) while ((desc = avcodec_descriptor_next(desc))) {
codecs[i++] = desc; codecs[i++] = desc;
}
switch_assert(i == nb_codecs); switch_assert(i == nb_codecs);
qsort(codecs, nb_codecs, sizeof(*codecs), compare_codec_desc); qsort(codecs, nb_codecs, sizeof(*codecs), compare_codec_desc);
*rcodecs = codecs; *rcodecs = codecs;
@ -1430,8 +1450,9 @@ static void print_codecs_for_id(switch_stream_handle_t *stream, enum AVCodecID i
stream->write_function(stream, " (%s: ", encoder ? "encoders" : "decoders"); stream->write_function(stream, " (%s: ", encoder ? "encoders" : "decoders");
while ((codec = next_codec_for_id(id, codec, encoder))) while ((codec = next_codec_for_id(id, codec, encoder))) {
stream->write_function(stream, "%s ", codec->name); stream->write_function(stream, "%s ", codec->name);
}
stream->write_function(stream, ")"); stream->write_function(stream, ")");
} }

View File

@ -282,8 +282,15 @@ static switch_status_t add_stream(MediaStream *mst, AVFormatContext *fc, AVCodec
int buffer_bytes = 2097152; /* 2 mb */ int buffer_bytes = 2097152; /* 2 mb */
int fps = 15; int fps = 15;
/* find the encoder */ if (mm->try_hardware_encoder && codec_id == AV_CODEC_ID_H264) {
*codec = avcodec_find_encoder(codec_id); *codec = avcodec_find_encoder_by_name("nvenc_h264");
}
if (!*codec) {
/* find the encoder */
*codec = avcodec_find_encoder(codec_id);
}
if (!(*codec)) { if (!(*codec)) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not find encoder for '%s'\n", avcodec_get_name(codec_id)); // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not find encoder for '%s'\n", avcodec_get_name(codec_id));
return status; return status;
@ -360,13 +367,15 @@ static switch_status_t add_stream(MediaStream *mst, AVFormatContext *fc, AVCodec
c->ticks_per_frame = 2; c->ticks_per_frame = 2;
c->coder_type = 1; // coder = 1
c->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop c->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop
c->me_cmp|= 1; // cmp=+chroma, where CHROMA = 1 c->me_cmp|= 1; // cmp=+chroma, where CHROMA = 1
c->me_method=ME_HEX; // me_method=hex
c->me_range = 16; // me_range=16 c->me_range = 16; // me_range=16
c->max_b_frames = 3; // bf=3 c->max_b_frames = 3; // bf=3
av_opt_set_int(c->priv_data, "b_strategy", 1, 0);
av_opt_set_int(c->priv_data, "motion_est", ME_HEX, 0);
av_opt_set_int(c->priv_data, "coder", 1, 0);
switch (mm->vprofile) { switch (mm->vprofile) {
case SWITCH_VIDEO_PROFILE_BASELINE: case SWITCH_VIDEO_PROFILE_BASELINE:
av_opt_set(c->priv_data, "profile", "baseline", 0); av_opt_set(c->priv_data, "profile", "baseline", 0);
@ -402,14 +411,12 @@ static switch_status_t add_stream(MediaStream *mst, AVFormatContext *fc, AVCodec
c->gop_size = 250; // g=250 c->gop_size = 250; // g=250
c->keyint_min = 25; // keyint_min=25 c->keyint_min = 25; // keyint_min=25
c->scenechange_threshold = 40; // sc_threshold=40
c->i_quant_factor = 0.71; // i_qfactor=0.71 c->i_quant_factor = 0.71; // i_qfactor=0.71
c->b_frame_strategy = 1; // b_strategy=1
c->qcompress = 0.6; // qcomp=0.6 c->qcompress = 0.6; // qcomp=0.6
c->qmin = 10; // qmin=10 c->qmin = 10; // qmin=10
c->qmax = 31; // qmax=31 c->qmax = 31; // qmax=31
c->max_qdiff = 4; // qdiff=4 c->max_qdiff = 4; // qdiff=4
av_opt_set(c->priv_data, "crf", "18", 0); av_opt_set_int(c->priv_data, "crf", 18, 0);
if (codec_id == AV_CODEC_ID_VP8) { if (codec_id == AV_CODEC_ID_VP8) {
@ -493,8 +500,18 @@ static switch_status_t open_audio(AVFormatContext *fc, AVCodec *codec, MediaStre
c = mst->st->codec; c = mst->st->codec;
ret = avcodec_open2(c, codec, NULL); ret = avcodec_open2(c, codec, NULL);
if (ret == AVERROR_EXPERIMENTAL) {
const AVCodecDescriptor *desc = avcodec_descriptor_get(c->codec_id);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Codec [%s] is experimental feature in libavcodec, never mind\n", desc->name);
c->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
ret = avcodec_open2(c, codec, NULL);
}
if (ret < 0) { if (ret < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open audio codec: %s\n", get_error_text(ret)); const AVCodecDescriptor *desc = avcodec_descriptor_get(c->codec_id);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open audio codec [%s], error: %s\n", desc->name, get_error_text(ret));
return status; return status;
} }
@ -677,21 +694,21 @@ static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void *
eh->video_st->frame->pts += delta; eh->video_st->frame->pts += delta;
} else { } else {
switch_core_timer_sync(eh->timer); switch_core_timer_sync(eh->timer);
if (eh->video_st->frame->pts == eh->timer->samplecount) { if (eh->video_st->frame->pts == eh->timer->samplecount) {
// never use the same pts, or the encoder coughs // never use the same pts, or the encoder coughs
eh->video_st->frame->pts++; eh->video_st->frame->pts++;
} else { } else {
uint64_t delta_tmp = eh->timer->samplecount - last_ts; uint64_t delta_tmp = eh->timer->samplecount - last_ts;
if (delta_tmp > 10) { if (delta_tmp > 10) {
delta = delta_tmp; delta = delta_tmp;
} }
eh->video_st->frame->pts = eh->timer->samplecount; eh->video_st->frame->pts = eh->timer->samplecount;
} }
} }
last_ts = eh->video_st->frame->pts; last_ts = eh->video_st->frame->pts;
//eh->video_st->frame->pts = switch_time_now() / 1000 - eh->video_st->next_pts; //eh->video_st->frame->pts = switch_time_now() / 1000 - eh->video_st->next_pts;
@ -709,7 +726,7 @@ static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void *
switch_mutex_lock(eh->mutex); switch_mutex_lock(eh->mutex);
ret = write_frame(eh->fc, &eh->video_st->st->codec->time_base, eh->video_st->st, &pkt); ret = write_frame(eh->fc, &eh->video_st->st->codec->time_base, eh->video_st->st, &pkt);
switch_mutex_unlock(eh->mutex); switch_mutex_unlock(eh->mutex);
av_free_packet(&pkt); av_packet_unref(&pkt);
} }
eh->in_callback = 0; eh->in_callback = 0;
@ -725,7 +742,7 @@ static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void *
av_init_packet(&pkt); av_init_packet(&pkt);
ret = avcodec_encode_video2(eh->video_st->st->codec, &pkt, eh->video_st->frame, &got_packet); ret = avcodec_encode_video2(eh->video_st->st->codec, &pkt, NULL, &got_packet);
if (ret < 0) { if (ret < 0) {
break; break;
@ -733,8 +750,10 @@ static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void *
switch_mutex_lock(eh->mutex); switch_mutex_lock(eh->mutex);
ret = write_frame(eh->fc, &eh->video_st->st->codec->time_base, eh->video_st->st, &pkt); ret = write_frame(eh->fc, &eh->video_st->st->codec->time_base, eh->video_st->st, &pkt);
switch_mutex_unlock(eh->mutex); switch_mutex_unlock(eh->mutex);
av_free_packet(&pkt); av_packet_unref(&pkt);
if (ret < 0) break; if (ret < 0) break;
} else {
break;
} }
} }
@ -1075,7 +1094,7 @@ SWITCH_STANDARD_APP(record_av_function)
if (got_packet) { if (got_packet) {
ret = write_frame(fc, &video_st.st->codec->time_base, video_st.st, &pkt); ret = write_frame(fc, &video_st.st->codec->time_base, video_st.st, &pkt);
av_free_packet(&pkt); av_packet_unref(&pkt);
goto again; goto again;
} }
} }
@ -1245,6 +1264,8 @@ struct av_file_context {
switch_image_t *last_img; switch_image_t *last_img;
int read_fps; int read_fps;
switch_time_t last_vid_push; switch_time_t last_vid_push;
int64_t seek_ts;
switch_bool_t read_paused;
}; };
typedef struct av_file_context av_file_context_t; typedef struct av_file_context av_file_context_t;
@ -1277,6 +1298,9 @@ static switch_status_t open_input_file(av_file_context_t *context, switch_file_h
switch_goto_status(SWITCH_STATUS_FALSE, err); switch_goto_status(SWITCH_STATUS_FALSE, err);
} }
handle->seekable = context->fc->iformat->read_seek2 ? 1 : (context->fc->iformat->read_seek ? 1 : 0);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "file %s is %sseekable\n", filename, handle->seekable ? "" : "not ");
/** Get information on the input file (number of streams etc.). */ /** Get information on the input file (number of streams etc.). */
if ((error = avformat_find_stream_info(context->fc, NULL)) < 0) { if ((error = avformat_find_stream_info(context->fc, NULL)) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open find stream info (error '%s')\n", get_error_text(error)); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open find stream info (error '%s')\n", get_error_text(error));
@ -1293,6 +1317,7 @@ static switch_status_t open_input_file(av_file_context_t *context, switch_file_h
context->video_st.st = context->fc->streams[i]; context->video_st.st = context->fc->streams[i];
if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) { if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) {
context->has_video = 1; context->has_video = 1;
handle->duration = av_rescale_q(context->video_st.st->duration, context->video_st.st->time_base, AV_TIME_BASE_Q);
} }
handle->mm.source_fps = ceil(av_q2d(context->video_st.st->avg_frame_rate)); handle->mm.source_fps = ceil(av_q2d(context->video_st.st->avg_frame_rate));
context->read_fps = (int)handle->mm.source_fps; context->read_fps = (int)handle->mm.source_fps;
@ -1346,14 +1371,18 @@ static switch_status_t open_input_file(av_file_context_t *context, switch_file_h
av_opt_set_int(resample_ctx, "in_channel_count", c->channels, 0); av_opt_set_int(resample_ctx, "in_channel_count", c->channels, 0);
av_opt_set_int(resample_ctx, "in_sample_rate", c->sample_rate, 0); av_opt_set_int(resample_ctx, "in_sample_rate", c->sample_rate, 0);
av_opt_set_int(resample_ctx, "in_sample_fmt", c->sample_fmt, 0); av_opt_set_int(resample_ctx, "in_sample_fmt", c->sample_fmt, 0);
av_opt_set_int(resample_ctx, "in_channel_layout", c->channel_layout, 0); av_opt_set_int(resample_ctx, "in_channel_layout",
(c->channel_layout == 0 && c->channels == 2) ? AV_CH_LAYOUT_STEREO : c->channel_layout, 0);
av_opt_set_int(resample_ctx, "out_channel_count", handle->channels, 0); av_opt_set_int(resample_ctx, "out_channel_count", handle->channels, 0);
av_opt_set_int(resample_ctx, "out_sample_rate", handle->samplerate,0); av_opt_set_int(resample_ctx, "out_sample_rate", handle->samplerate,0);
av_opt_set_int(resample_ctx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); av_opt_set_int(resample_ctx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
av_opt_set_int(resample_ctx, "out_channel_layout", handle->channels == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO, 0); av_opt_set_int(resample_ctx, "out_channel_layout", handle->channels == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO, 0);
if ((ret = avresample_open(resample_ctx)) < 0) { if ((ret = avresample_open(resample_ctx)) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize the resampling context\n"); char errbuf[1024];
av_strerror(ret, errbuf, 1024);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize the resampling context, ret=%d: %s\n", ret, errbuf);
av_free(resample_ctx); av_free(resample_ctx);
switch_goto_status(SWITCH_STATUS_FALSE, err); switch_goto_status(SWITCH_STATUS_FALSE, err);
} }
@ -1392,11 +1421,32 @@ static void *SWITCH_THREAD_FUNC file_read_thread_run(switch_thread_t *thread, vo
while (context->file_read_thread_running && !context->closed) { while (context->file_read_thread_running && !context->closed) {
int vid_frames = 0; int vid_frames = 0;
if (context->seek_ts >= 0) {
int stream_id = -1;
switch_mutex_lock(context->mutex);
switch_buffer_zero(context->audio_buffer);
switch_mutex_unlock(context->mutex);
if (context->eh.video_queue) {
flush_video_queue(context->eh.video_queue, 0);
}
// if (context->has_audio) stream_id = context->audio_st.st->index;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "seeking to %" SWITCH_INT64_T_FMT "\n", context->seek_ts);
avformat_seek_file(context->fc, stream_id, 0, context->seek_ts, INT64_MAX, 0);
context->seek_ts = -2;
context->video_st.next_pts = 0;
context->video_start_time = 0;
avcodec_flush_buffers(context->video_st.st->codec);
}
if (context->has_video) { if (context->has_video) {
vid_frames = switch_queue_size(context->eh.video_queue); vid_frames = switch_queue_size(context->eh.video_queue);
} }
if (switch_buffer_inuse(context->audio_buffer) > AUDIO_BUF_SEC * context->audio_st.sample_rate * context->audio_st.channels * 2 && if (switch_buffer_inuse(context->audio_buffer) > AUDIO_BUF_SEC * context->audio_st.sample_rate * context->audio_st.channels * 2 &&
(!context->has_video || vid_frames > 5)) { (!context->has_video || vid_frames > 5)) {
switch_yield(context->has_video ? 1000 : 10000); switch_yield(context->has_video ? 1000 : 10000);
continue; continue;
@ -1434,13 +1484,13 @@ again:
if ((error = avcodec_decode_video2(context->video_st.st->codec, vframe, &got_data, &pkt)) < 0) { if ((error = avcodec_decode_video2(context->video_st.st->codec, vframe, &got_data, &pkt)) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not decode frame (error '%s')\n", get_error_text(error)); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not decode frame (error '%s')\n", get_error_text(error));
av_free_packet(&pkt); av_packet_unref(&pkt);
av_frame_free(&vframe); av_frame_free(&vframe);
break; break;
} }
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pkt: %d, pts: %lld dts: %lld\n", pkt.size, pkt.pts, pkt.dts); // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pkt: %d, pts: %lld dts: %lld\n", pkt.size, pkt.pts, pkt.dts);
av_free_packet(&pkt); av_packet_unref(&pkt);
//if (switch_queue_size(context->eh.video_queue) > 300) { //if (switch_queue_size(context->eh.video_queue) > 300) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Dropping frames\n"); // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Dropping frames\n");
@ -1495,7 +1545,7 @@ again:
img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, vframe->width, vframe->height, 1); img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, vframe->width, vframe->height, 1);
if (img) { if (img) {
uint64_t *pts = malloc(sizeof(uint64_t)); int64_t *pts = malloc(sizeof(int64_t));
if (pts) { if (pts) {
#ifdef ALT_WAY #ifdef ALT_WAY
@ -1537,12 +1587,12 @@ again:
if ((error = avcodec_decode_audio4(context->audio_st.st->codec, &in_frame, &got_data, &pkt)) < 0) { if ((error = avcodec_decode_audio4(context->audio_st.st->codec, &in_frame, &got_data, &pkt)) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not decode frame (error '%s')\n", get_error_text(error)); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not decode frame (error '%s')\n", get_error_text(error));
av_free_packet(&pkt); av_packet_unref(&pkt);
break; break;
} }
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pkt: %d, decodedddd: %d pts: %lld dts: %lld\n", pkt.size, error, pkt.pts, pkt.dts); // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pkt: %d, decodedddd: %d pts: %lld dts: %lld\n", pkt.size, error, pkt.pts, pkt.dts);
av_free_packet(&pkt); av_packet_unref(&pkt);
if (got_data) { if (got_data) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "got data frm->format: %d samples: %d\n", in_frame.format, in_frame.nb_samples); // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "got data frm->format: %d samples: %d\n", in_frame.format, in_frame.nb_samples);
@ -1624,7 +1674,7 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa
memset(context, 0, sizeof(av_file_context_t)); memset(context, 0, sizeof(av_file_context_t));
handle->private_info = context; handle->private_info = context;
context->pool = handle->memory_pool; context->pool = handle->memory_pool;
context->seek_ts = -1;
context->offset = DFT_RECORD_OFFSET; context->offset = DFT_RECORD_OFFSET;
if (handle->params && (tmp = switch_event_get_header(handle->params, "av_video_offset"))) { if (handle->params && (tmp = switch_event_get_header(handle->params, "av_video_offset"))) {
context->offset = atoi(tmp); context->offset = atoi(tmp);
@ -1699,7 +1749,7 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa
const AVCodecDescriptor *desc; const AVCodecDescriptor *desc;
if ((handle->stream_name && (!strcasecmp(handle->stream_name, "rtmp") || !strcasecmp(handle->stream_name, "youtube")))) { if ((handle->stream_name && (!strcasecmp(handle->stream_name, "rtmp") || !strcasecmp(handle->stream_name, "youtube")))) {
if (fmt->video_codec != AV_CODEC_ID_H264 ) { if (fmt->video_codec != AV_CODEC_ID_H264 ) {
fmt->video_codec = AV_CODEC_ID_H264; // force H264 fmt->video_codec = AV_CODEC_ID_H264; // force H264
} }
@ -1915,6 +1965,15 @@ static switch_status_t av_file_command(switch_file_handle_t *handle, switch_file
switch_buffer_zero(context->audio_buffer); switch_buffer_zero(context->audio_buffer);
switch_mutex_unlock(context->mutex); switch_mutex_unlock(context->mutex);
break; break;
case SCFC_PAUSE_READ:
if (context->read_paused) {
context->read_paused = SWITCH_FALSE;
context->video_st.next_pts = 0;
context->video_start_time = 0;
} else {
context->read_paused = SWITCH_TRUE;
}
break;
default: default:
break; break;
} }
@ -1970,7 +2029,15 @@ static switch_status_t av_file_close(switch_file_handle_t *handle)
static switch_status_t av_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence) static switch_status_t av_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence)
{ {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "seek not implemented\n"); av_file_context_t *context = (av_file_context_t *)handle->private_info;
if (whence == SEEK_SET) {
handle->pos = handle->offset_pos = samples;
}
context->seek_ts = samples / handle->native_rate * AV_TIME_BASE;
*cur_sample = context->seek_ts;
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
} }
@ -2067,6 +2134,7 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f
switch_status_t status = SWITCH_STATUS_SUCCESS; switch_status_t status = SWITCH_STATUS_SUCCESS;
double fl_to = 0.02; double fl_to = 0.02;
int do_fl = 0; int do_fl = 0;
int smaller_ts = context->read_fps;
if (!context->has_video) return SWITCH_STATUS_FALSE; if (!context->has_video) return SWITCH_STATUS_FALSE;
@ -2074,13 +2142,74 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f
return SWITCH_STATUS_BREAK; return SWITCH_STATUS_BREAK;
} }
fl_to = (1000 / context->read_fps) * 1000; if (handle->mm.fps > 0 && handle->mm.fps < smaller_ts) {
smaller_ts = handle->mm.fps;
}
fl_to = (1000 / smaller_ts) * 1000;
//printf("WTF %d (%f)\n",switch_queue_size(context->eh.video_queue), fl_to); //printf("WTF %d (%f)\n",switch_queue_size(context->eh.video_queue), fl_to);
if (flags & SVR_FLUSH) { if (flags & SVR_FLUSH) {
max_delta = fl_to; max_delta = fl_to;
do_fl = 1; do_fl = 1;
} }
if (!context->file_read_thread_running && switch_queue_size(context->eh.video_queue) == 0) {
return SWITCH_STATUS_FALSE;
}
if (context->read_paused) {
int sanity = 10;
if (context->seek_ts == -2) { // just seeked, try read a new img
again1:
status = switch_queue_trypop(context->eh.video_queue, &pop);
if (pop && status == SWITCH_STATUS_SUCCESS) {
context->seek_ts = -1;
switch_img_free(&context->last_img);
context->last_img = (switch_image_t *)pop;
switch_img_copy(context->last_img, &frame->img);
context->vid_ready = 1;
return SWITCH_STATUS_SUCCESS;
}
if (context->last_img) { // repeat the last img
switch_img_copy(context->last_img, &frame->img);
context->vid_ready = 1;
context->seek_ts = -1;
return SWITCH_STATUS_SUCCESS;
}
if ((flags & SVR_BLOCK) && sanity-- > 0) {
switch_yield(10000);
goto again1;
}
return SWITCH_STATUS_BREAK;
}
if (context->last_img) { // repeat the last img
if ((flags & SVR_BLOCK)) switch_yield(100000);
switch_img_copy(context->last_img, &frame->img);
context->vid_ready = 1;
return SWITCH_STATUS_SUCCESS;
}
if ((flags & SVR_BLOCK)) {
status = switch_queue_pop(context->eh.video_queue, &pop);
} else {
status = switch_queue_trypop(context->eh.video_queue, &pop);
}
if (pop && status == SWITCH_STATUS_SUCCESS) {
context->last_img = (switch_image_t *)pop;
switch_img_copy(context->last_img, &frame->img);
context->vid_ready = 1;
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_BREAK;
}
if (context->last_img) { if (context->last_img) {
if (mst->next_pts && (switch_time_now() - mst->next_pts > max_delta)) { if (mst->next_pts && (switch_time_now() - mst->next_pts > max_delta)) {
switch_img_free(&context->last_img); // too late switch_img_free(&context->last_img); // too late
@ -2102,10 +2231,6 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f
} }
} }
if (!context->file_read_thread_running && switch_queue_size(context->eh.video_queue) == 0) {
return SWITCH_STATUS_FALSE;
}
if (st->codec->time_base.num) { if (st->codec->time_base.num) {
ticks = st->parser ? st->parser->repeat_pict + 1 : st->codec->ticks_per_frame; ticks = st->parser ? st->parser->repeat_pict + 1 : st->codec->ticks_per_frame;
// mst->next_pts += ((int64_t)AV_TIME_BASE * st->codec->time_base.num * ticks) / st->codec->time_base.den; // mst->next_pts += ((int64_t)AV_TIME_BASE * st->codec->time_base.num * ticks) / st->codec->time_base.den;
@ -2132,6 +2257,7 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f
pts = av_rescale_q(*((uint64_t *)img->user_priv), st->time_base, AV_TIME_BASE_Q); pts = av_rescale_q(*((uint64_t *)img->user_priv), st->time_base, AV_TIME_BASE_Q);
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "pkt_pts: %lld pts: %lld queue size: %u\n", *((uint64_t *)img->user_priv), pts, switch_queue_size(context->eh.video_queue)); // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "pkt_pts: %lld pts: %lld queue size: %u\n", *((uint64_t *)img->user_priv), pts, switch_queue_size(context->eh.video_queue));
handle->vpos = pts;
if (!context->video_start_time) { if (!context->video_start_time) {
context->video_start_time = now - pts; context->video_start_time = now - pts;
@ -2150,10 +2276,10 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f
if (pts == 0 || context->video_start_time == 0) mst->next_pts = 0; if (pts == 0 || context->video_start_time == 0) mst->next_pts = 0;
if ((mst->next_pts && (now - mst->next_pts) > max_delta)) { if ((mst->next_pts && (now - mst->next_pts) > max_delta)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "picture is too late, off: %" SWITCH_INT64_T_FMT " max delta: %" SWITCH_INT64_T_FMT " queue size:%u\n", (int64_t)(now - mst->next_pts), max_delta, switch_queue_size(context->eh.video_queue)); //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "picture is too late, off: %" SWITCH_INT64_T_FMT " max delta: %" SWITCH_INT64_T_FMT " queue size:%u fps:%u/%0.2f\n", (int64_t)(now - mst->next_pts), max_delta, switch_queue_size(context->eh.video_queue), context->read_fps, handle->mm.fps);
switch_img_free(&img); switch_img_free(&img);
max_delta = AV_TIME_BASE; //max_delta = AV_TIME_BASE;
if (switch_queue_size(context->eh.video_queue) > 0) { if (switch_queue_size(context->eh.video_queue) > 0) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "WTF again\n"); // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "WTF again\n");
goto again; goto again;
@ -2161,7 +2287,7 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f
mst->next_pts = 0; mst->next_pts = 0;
context->video_start_time = 0; context->video_start_time = 0;
return SWITCH_STATUS_BREAK; return SWITCH_STATUS_BREAK;
} }
} }
if ((flags & SVR_BLOCK) || do_fl) { if ((flags & SVR_BLOCK) || do_fl) {

View File

@ -2,7 +2,7 @@ include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_avmd MODNAME=mod_avmd
mod_LTLIBRARIES = mod_avmd.la mod_LTLIBRARIES = mod_avmd.la
mod_avmd_la_SOURCES = mod_avmd.c amplitude.c buffer.c desa2.c goertzel.c fast_acosf.c mod_avmd_la_SOURCES = mod_avmd.c avmd_buffer.c avmd_desa2_tweaked.c avmd_fast_acosf.c
mod_avmd_la_CFLAGS = $(AM_CFLAGS) $(AM_MOD_AVMD_CXXFLAGS) mod_avmd_la_CFLAGS = $(AM_CFLAGS) $(AM_MOD_AVMD_CXXFLAGS)
mod_avmd_la_LIBADD = $(switch_builddir)/libfreeswitch.la mod_avmd_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_avmd_la_LDFLAGS = -avoid-version -module -no-undefined -shared mod_avmd_la_LDFLAGS = -avoid-version -module -no-undefined -shared

View File

@ -1,10 +0,0 @@
#ifndef __AMPLITUDE_H__
#define __AMPLITUDE_H__
#include "buffer.h"
extern double amplitude(circ_buffer_t *, size_t i, double f);
#endif

View File

@ -1,7 +1,9 @@
#ifndef __AMPLITUDE_H__ #ifndef __AVMD_AMPLITUDE_H__
#include <math.h> #include <math.h>
#include "amplitude.h" #include "avmd_amplitude.h"
#include "psi.h" #include "avmd_psi.h"
/*! \brief /*! \brief
* @author Eric des Courtis * @author Eric des Courtis
@ -10,14 +12,13 @@
* @param f Frequency estimate * @param f Frequency estimate
* @return The amplitude at position i * @return The amplitude at position i
*/ */
extern double amplitude(circ_buffer_t *b, size_t i, double f) extern double avmd_amplitude(circ_buffer_t *b, size_t i, double f)
{ {
double result; double result;
result = sqrt(PSI(b, i) / sin(f * f)); result = sqrt(PSI(b, i) / sin(f * f));
return result; return result;
} }
#endif
#endif /* __AVMD_AMPLITUDE_H__ */

View File

@ -0,0 +1,18 @@
/*
* @brief Estimation of amplitude using DESA-2 algorithm.
* @author Eric des Courtis
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
*/
#ifndef __AVMD_AMPLITUDE_H__
#define __AVMD_AMPLITUDE_H__
#include "avmd_buffer.h"
extern double avmd_amplitude(circ_buffer_t *, size_t i, double f);
#endif /* __AVMD_AMPLITUDE_H__ */

View File

@ -1,5 +1,5 @@
#ifndef __BUFFER_H__ #ifndef __BUFFER_H__
#include "buffer.h" #include "avmd_buffer.h"
#endif #endif
extern size_t next_power_of_2(size_t v) extern size_t next_power_of_2(size_t v)

View File

@ -1,5 +1,15 @@
#ifndef __BUFFER_H__ /*
#define __BUFFER_H__ * @brief Circular buffer.
*
* @author Eric des Courtis
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
*/
#ifndef __AVMD_BUFFER_H__
#define __AVMD_BUFFER_H__
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
@ -55,6 +65,10 @@ extern size_t next_power_of_2(size_t v);
if ((b)->backlog > (b)->buf_len) (b)->backlog = (b)->buf_len; \ if ((b)->backlog > (b)->buf_len) (b)->backlog = (b)->buf_len; \
} while (0) } while (0)
/* ((f)[(b)->i] >= 0) ? \
((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MAX): \
(0.0 - ((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MIN)) \ */
#define INSERT_INT16_FRAME(b, f, l) \ #define INSERT_INT16_FRAME(b, f, l) \
{ \ { \
for ((b)->i = 0; (b)->i < (l); (b)->i++) { \ for ((b)->i = 0; (b)->i < (l); (b)->i++) { \
@ -62,9 +76,7 @@ extern size_t next_power_of_2(size_t v);
(b), \ (b), \
((b)->i + (b)->pos), \ ((b)->i + (b)->pos), \
( \ ( \
((f)[(b)->i] >= 0) ? \ (BUFF_TYPE)(f)[(b)->i] \
((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MAX): \
(0.0 - ((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MIN)) \
) \ ) \
); \ ); \
} \ } \
@ -92,14 +104,14 @@ extern size_t next_power_of_2(size_t v);
//#define DESTROY_CIRC_BUFFER(b) free((b)->buf) //#define DESTROY_CIRC_BUFFER(b) free((b)->buf)
#define GET_BACKLOG_POS(b) ((b)->lpos - (b)->backlog) #define GET_BACKLOG_POS(b) ((b)->lpos - (b)->backlog)
#define GET_CURRENT_POS(b) ((b)->lpos) #define GET_CURRENT_POS(b) ((b)->pos)
#define GET_CURRENT_SAMPLE(b) GET_SAMPLE((b), GET_CURRENT_POS((b))) #define GET_CURRENT_LPOS(b) ((b)->lpos)
#define GET_CURRENT_SAMPLE(b) GET_SAMPLE((b), GET_CURRENT_LPOS((b)))
#define ADD_SAMPLE(b, s) \ #define ADD_SAMPLE(b, s) \
do { \ do { \
INC_POS((b)); \ INC_POS((b)); \
SET_SAMPLE((b), GET_CURRENT_POS((b)), (s)); \ SET_SAMPLE((b), GET_CURRENT_LPOS((b)), (s)); \
} while (0) } while (0)
#endif #endif /* __AVMD_BUFFER_H__ */

View File

@ -1,4 +1,6 @@
#ifndef __DESA2_H__ #ifndef __AVMD_DESA2_H__
#include <stdio.h> #include <stdio.h>
#ifdef WIN32 #ifdef WIN32
#include <float.h> #include <float.h>
@ -6,15 +8,15 @@
#else #else
#define ISNAN(x) (isnan(x)) #define ISNAN(x) (isnan(x))
#endif #endif
#include "buffer.h" #include "avmd_buffer.h"
#include "desa2.h" #include "avmd_desa2.h"
#include "options.h" #include "avmd_options.h"
#ifdef FASTMATH #ifdef AVMD_FAST_MATH
#include "fast_acosf.h" #include "avmd_fast_acosf.h"
#endif #endif
extern double desa2(circ_buffer_t *b, size_t i) extern double avmd_desa2(circ_buffer_t *b, size_t i)
{ {
double d; double d;
double n; double n;
@ -33,14 +35,11 @@ extern double desa2(circ_buffer_t *b, size_t i)
x4 = GET_SAMPLE((b), ((i) + 4)); x4 = GET_SAMPLE((b), ((i) + 4));
x2sq = x2 * x2; x2sq = x2 * x2;
d = 2.0 * ((x2sq) - (x1 * x3)); d = 2.0 * ((x2sq) - (x1 * x3));
if (d == 0.0) return 0.0; if (d == 0.0) return 0.0;
n = ((x2sq) - (x0 * x4)) - ((x1 * x1) - (x0 * x2)) - ((x3 * x3) - (x2 * x4)); n = ((x2sq) - (x0 * x4)) - ((x1 * x1) - (x0 * x2)) - ((x3 * x3) - (x2 * x4));
#ifdef AVMD_FAST_MATH
#ifdef FASTMATH
result = 0.5 * (double)fast_acosf((float)n/d); result = 0.5 * (double)fast_acosf((float)n/d);
#else #else
result = 0.5 * acos(n/d); result = 0.5 * acos(n/d);
@ -52,4 +51,4 @@ extern double desa2(circ_buffer_t *b, size_t i)
} }
#endif #endif /* __AVMD_DESA2_H__ */

View File

@ -0,0 +1,19 @@
/*
* @brief DESA-2 algorithm implementation.
* @author Eric des Courtis
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
*/
#ifndef __AVMD_DESA2_H__
#define __AVMD_DESA2_H__
#include <math.h>
#include "avmd_buffer.h"
/* Returns digital frequency estimation. */
extern double avmd_desa2(circ_buffer_t *b, size_t i);
#endif /* __AVMD_DESA2_H__ */

View File

@ -0,0 +1,64 @@
#ifndef __AVMD_DESA2_TWEAKED_H__
#include <stdio.h>
#ifdef WIN32
#include <float.h>
#define ISNAN(x) (!!(_isnan(x)))
#else
#define ISNAN(x) (isnan(x))
#endif
#include "avmd_buffer.h"
#include "avmd_desa2_tweaked.h"
#include "avmd_options.h"
#ifdef AVMD_FAST_MATH
#include "avmd_fast_acosf.h"
#endif
#include <switch.h>
double
avmd_desa2_tweaked(circ_buffer_t *b, size_t i,
switch_core_session_t *session)
{
double d;
double n;
double x0;
double x1;
double x2;
double x3;
double x4;
double x2sq;
double result;
x0 = GET_SAMPLE((b), (i));
x1 = GET_SAMPLE((b), ((i) + 1));
x2 = GET_SAMPLE((b), ((i) + 2));
x3 = GET_SAMPLE((b), ((i) + 3));
x4 = GET_SAMPLE((b), ((i) + 4));
x2sq = x2 * x2;
d = 2.0 * ((x2sq) - (x1 * x3));
n = ((x2sq) - (x0 * x4)) - ((x1 * x1)
- (x0 * x2)) - ((x3 * x3) - (x2 * x4));
/* instead of
#ifdef FASTMATH
result = 0.5 * (double)fast_acosf((float)n/d);
#else
result = 0.5 * acos(n/d);
#endif
we do simplified, modified for speed version : */
result = n/d;
if (isinf(result)) {
if (n < 0.0)
return -10.0;
else
return 10.0;
}
return result;
}
#endif /* __AVMD_DESA2_TWEAKED_H__ */

View File

@ -0,0 +1,42 @@
/*
* @brief Estimator of cosine of digital frequency.
* @details It is tweaked DESA implementation which
* returns partial product of DESA-2 estimation
* so that arc cosine transform can be ommited
* on all computations, but these values can
* be checked for convergence in the same time.
* If the partial results converge then frequency
* converges too.
* @author Piotr Gregor < piotrek.gregor gmail.com >
* @date 20 Mar 2016
*/
#ifndef __AVMD_DESA2_TWEAKED_H__
#define __AVMD_DESA2_TWEAKED_H__
#include <math.h>
#include "avmd_buffer.h"
#include <switch.h>
/* Instead of returning digital frequency estimation using
* result = 0.5 * acos(n/d),
* which involves expensive computation of arc cosine on
* each new sample, this function returns only (n/d) factor.
* The series of these partial DESA-2 results can be still
* checked for convergence, though measures and thresholds
* used to assess this will differ from those used for
* assessment of convergence of instantaneous frequency
* estimates since transformation of tweaked results
* to corresponding frequencies is nonlinear.
* The actual frequency estimation can be retrieved later
* from this partial result using
* 0.5 * acos(n/d)
*/
double avmd_desa2_tweaked(circ_buffer_t *b, size_t i,
switch_core_session_t *session);
#endif /* __AVMD_DESA2_TWEAKED_H__ */

View File

@ -0,0 +1,320 @@
#include <switch.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef _MSC_VER
#include <stdint.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef _MSC_VER
#include <sys/mman.h>
#endif
#include <assert.h>
#include <errno.h>
#include <math.h>
#include <string.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include "avmd_fast_acosf.h"
#include "avmd_options.h"
#ifdef AVMD_FAST_MATH
typedef union {
uint32_t i;
float f;
} float_conv_t;
/*
* Manipulate these parameters to change
* mapping's resolution. The sine tone
* of 1600Hz is detected even with 20
* bits discarded in float integer representation
* with only slightly increased amount of false
* positives (keeping variance threshold on 0.0001).
* 12 bits seem to be good choice when there is
* a need to compute faster and/or decrease mapped file
* size on disk while keeping false positives low.
*/
#define ACOS_TABLE_CONST_EXPONENT (0x70)
#define ACOS_TABLE_CONST_EXPONENT_BITS (3)
#define ACOS_TABLE_DISCARDED_BITS (3)
/* rosolution:
3: 15 728 640 indices spreading range [0.0, 1.0], table size on disk 134 217 728 bytes (default)
4: 7 364 320 indices spreading range [0.0, 1.0], table size on disk 67 108 864 bytes
5: 3 932 160 indices spreading range [0.0, 1.0], table size on disk 33 554 432 bytes
12: 30 720 indices spreading range [0.0, 1.0], table size on disk 262 144 bytes
16: 1 920 indices spreading range [0.0, 1.0], table size on disk 16 384 bytes
20: 120 indices spreading range [0.0, 1.0], table size on disk 1 024 bytes
24: 7 indices spreading range [0.0, 1.0], table size on disk 64 bytes
26: 1 indices spreading range [0.0, 1.0], table size on disk 16 bytes
*/
#define ACOS_TABLE_FREE_EXPONENT_BITS (7 - ACOS_TABLE_CONST_EXPONENT_BITS)
#define ACOS_TABLE_DATA_BITS (31 - ACOS_TABLE_CONST_EXPONENT_BITS - ACOS_TABLE_DISCARDED_BITS)
#define ACOS_TABLE_LENGTH (1 << (31 - ACOS_TABLE_CONST_EXPONENT_BITS - ACOS_TABLE_DISCARDED_BITS))
#define VARIA_DATA_MASK (0x87FFFFFF & ~((1 << ACOS_TABLE_DISCARDED_BITS) - 1))
#define CONST_DATA_MASK (((1 << ACOS_TABLE_CONST_EXPONENT_BITS) - 1) \
<< (ACOS_TABLE_DATA_BITS - 1 + ACOS_TABLE_DISCARDED_BITS))
#define SIGN_UNPACK_MASK (1 << (ACOS_TABLE_DATA_BITS - 1))
#define DATA_UNPACK_MASK ((1 << (ACOS_TABLE_DATA_BITS - 1)) - 1)
#define SIGN_MASK (0x80000000)
#define DATA_MASK (DATA_UNPACK_MASK << ACOS_TABLE_DISCARDED_BITS)
#define ACOS_TABLE_FILENAME "/tmp/acos_table.dat"
static uint32_t index_from_float(float f);
static float float_from_index(uint32_t d);
static float *acos_table = NULL;
static int acos_fd = -1;
#ifdef FAST_ACOSF_TESTING
#define INF(x) printf("[%s] [%u]\n", #x, x)
#define INFX(x) printf("[%s] [%08x]\n", #x, x)
static void
debug_print(void);
static void
dump_table_summary(void);
#endif /* FAST_ACOSF_TESTING */
extern int compute_table(void)
{
uint32_t i;
float f;
FILE *acos_table_file;
size_t res;
acos_table_file = fopen(ACOS_TABLE_FILENAME, "w");
for (i = 0; i < ACOS_TABLE_LENGTH; i++) {
f = acosf(float_from_index(i));
res = fwrite(&f, sizeof(f), 1, acos_table_file);
if (res != 1) {
goto fail;
}
}
res = fclose(acos_table_file);
if (res != 0) {
return -2;
}
return 0;
fail:
fclose(acos_table_file);
return -1;
}
extern int init_fast_acosf(void)
{
int ret, errsv;
FILE *acos_fp;
char err[150];
if (acos_table == NULL) {
ret = access(ACOS_TABLE_FILENAME, F_OK);
if (ret == -1) {
/* file doesn't exist, bad permissions,
* or some other error occured */
errsv = errno;
strerror_r(errsv, err, 150);
if (errsv != ENOENT) return -1;
else {
switch_log_printf(
SWITCH_CHANNEL_LOG,
SWITCH_LOG_NOTICE,
"File [%s] doesn't exist. Creating file...\n", ACOS_TABLE_FILENAME
);
ret = compute_table();
if (ret != 0) return -2;
}
} else {
switch_log_printf(
SWITCH_CHANNEL_LOG,
SWITCH_LOG_INFO,
"Using previously created file [%s]\n", ACOS_TABLE_FILENAME
);
}
}
acos_fp = fopen(ACOS_TABLE_FILENAME, "r");
if (acos_fp == NULL) return -3;
/* can't fail */
acos_fd = fileno(acos_fp);
acos_table = (float *) mmap(
NULL, /* kernel chooses the address at which to create the mapping */
ACOS_TABLE_LENGTH * sizeof(float),
PROT_READ,
MAP_SHARED | MAP_POPULATE, /* read-ahead on the file. Later accesses to the mapping
* will not be blocked by page faults */
acos_fd,
0
);
if (acos_table == MAP_FAILED) return -4;
return 0;
}
extern int destroy_fast_acosf(void)
{
if (munmap(acos_table, ACOS_TABLE_LENGTH) == -1) return -1;
if (acos_fd != -1) {
if (close(acos_fd) == -1) return -2;
}
/* disable use of fast arc cosine file */
acos_table = NULL;
return 0;
}
extern float fast_acosf(float x)
{
return acos_table[index_from_float(x)];
}
static uint32_t index_from_float(float f)
{
float_conv_t d;
d.f = f;
return ((d.i & SIGN_MASK) >> (32 - ACOS_TABLE_DATA_BITS)) |
((d.i & DATA_MASK) >> ACOS_TABLE_DISCARDED_BITS);
}
static float float_from_index(uint32_t d)
{
float_conv_t f;
f.i = ((d & SIGN_UNPACK_MASK) << (32 - ACOS_TABLE_DATA_BITS)) |
((d & DATA_UNPACK_MASK) << ACOS_TABLE_DISCARDED_BITS) | CONST_DATA_MASK;
return f.f;
}
#ifdef FAST_ACOSF_TESTING
#define INF(x) printf("[%s] [%u]\n", #x, x)
#define INFX(x) printf("[%s] [%08x]\n", #x, x)
static void
debug_print(void)
{
INF(ACOS_TABLE_CONST_EXPONENT);
INF(ACOS_TABLE_CONST_EXPONENT_BITS);
INF(ACOS_TABLE_FREE_EXPONENT_BITS);
INF(ACOS_TABLE_DISCARDED_BITS);
INF(ACOS_TABLE_DATA_BITS);
INF(ACOS_TABLE_LENGTH);
INFX(VARIA_DATA_MASK);
INFX(CONST_DATA_MASK);
INFX(SIGN_UNPACK_MASK);
INFX(DATA_UNPACK_MASK);
INFX(SIGN_MASK);
INFX(DATA_MASK);
}
static void
dump_table_summary(void)
{
uint32_t i, i_0, i_1, di;
float f;
i = 1;
i_0 = index_from_float(0.0);
i_1 = index_from_float(1.0);
di = (i_1 - i_0)/100;
if (di == 0) di = 1;
for (; i < ACOS_TABLE_LENGTH; i += di )
{
f = float_from_index(i);
printf("-01i[%.10u] : ffi[%f] fa[%f] acos[%f]\n",
i, f, fast_acosf(f), acos(f));
}
i = 1;
for (; i < ACOS_TABLE_LENGTH; i = (i << 1))
{
f = fast_acosf(float_from_index(i));
printf("--i[%.10u] : fa[%f] ffi[%f]\n",
i, f, float_from_index(i));
}
f = 0.0;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.1;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.2;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.3;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.4;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.5;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.6;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.7;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 7.5;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.8;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.9;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.95;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.99;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 1.0;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 1.1;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 1.2;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = 0.0;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.1;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.2;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.3;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.4;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.5;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.6;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.7;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -7.5;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.8;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.9;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.95;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -0.99;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -1.0;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -1.1;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
f = -1.2;
printf("i [%d] from float [%f]\n", index_from_float(f), f);
}
#endif /* FAST_ACOSF_TESTING */
#endif

View File

@ -0,0 +1,53 @@
/*
* @brief Fast arithmetic using precomputed arc cosine table.
* @author Eric des Courtis
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
*/
#ifndef __AVMD_FAST_ACOSF_H__
#define __AVMD_FAST_ACOSF_H__
#define ACOS_TABLE_FILENAME "/tmp/acos_table.dat"
/*! \brief Arc cosine table initialization.
*
* @author Eric des Courtis
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
* @return 0 on success, negative value otherwise:
* -1 can't access arc cos table with error != NOENT,
* -2 table creation failed (compute_table)
* -3 can access table but fopen failed
* -4 mmap failed
*/
extern int init_fast_acosf(void);
/*! \brief Arc cosine table deinitialization.
*
* @author Eric des Courtis
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
* @return 0 on success, negative value otherwise:
* -1 munmap failed,
* -2 close failed
*/
extern int destroy_fast_acosf(void);
/*! \brief Return arc cos for this argument.
* @details Uses previously created and mmapped file.
* @author Eric des Courtis
*/
extern float fast_acosf(float x);
/*! \brief Arc cosine table creation.
*
* @author Eric des Courtis
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
* @return 0 on success, negative value otherwise:
* -1 fwrite failed,
* -2 fclose failed
*/
extern int compute_table(void);
#endif /* __AVMD_FAST_ACOSF_H__ */

View File

@ -0,0 +1,17 @@
/*
* @brief Filters.
* @author Piotr Gregor < piotrek.gregor gmail.com >
* @date 23 Mar 2016
*/
#ifndef __AVMD_FIR_H__
#define __AVMD_FIR_H__
#define DESA_MAX(a, b) (a) > (b) ? (a) : (b)
#define MEDIAN_FILTER(a, b, c) (a) > (b) ? ((a) > (c) ? \
DESA_MAX((b), (c)) : a) : ((b) > (c) ? DESA_MAX((a), (c)) : (b))
#endif

View File

@ -0,0 +1,30 @@
#ifndef __AVMD_GOERTZEL_H__
#include <math.h>
#include "avmd_goertzel.h"
#include "avmd_buffer.h"
extern double avmd_goertzel(circ_buffer_t *b, size_t pos, double f, size_t num)
{
double s = 0.0;
double p = 0.0;
double p2 = 0.0;
double coeff;
size_t i;
coeff = 2.0 * cos(2.0 * M_PI * f);
for (i = 0; i < num; i++) {
/* TODO: optimize to avoid GET_SAMPLE when possible */
s = GET_SAMPLE(b, i + pos) + (coeff * p) - p2;
p2 = p;
p = s;
}
return (p2 * p2) + (p * p) - (coeff * p2 * p);
}
#endif /* __AVMD_GOERTZEL_H__ */

View File

@ -0,0 +1,33 @@
/*
* @brief Goertzel algorithm.
* @author Eric des Courtis
*/
#ifndef __AVMD_GOERTZEL_H__
#define __AVMD_GOERTZEL_H__
#ifndef _MSC_VER
#include <stdint.h>
#endif
#include "avmd_buffer.h"
#if !defined(M_PI)
/* C99 systems may not define M_PI */
#define M_PI 3.14159265358979323846264338327
#endif
/*! \brief Identify frequency components of a signal
* @author Eric des Courtis
* @param b A circular buffer
* @param pos Position in the buffer
* @param f Frequency to look at
* @param num Number of samples to look at
* @return A power estimate for frequency f at position pos in the stream
*/
extern double avmd_goertzel(circ_buffer_t *b, size_t pos, double f, size_t num);
#endif /* __AVMD_GOERTZEL_H__ */

View File

@ -0,0 +1,49 @@
/*
* @brief Options controlling avmd module.
*
* @author Eric des Courtis
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
*/
#ifndef __AVMD_OPTIONS_H__
#define __AVMD_OPTIONS_H__
/* define/undefine this to enable/disable printing of avmd
* intermediate computations to log */
/*#define AVMD_DEBUG */
/* define/undef this to enable/disable reporting of beep
* detection status after session ended */
#define AVMD_REPORT_STATUS
/* define/undefine this to enable/disable faster computation
* of arcus cosine - table will be created mapping floats
* to integers and returning arc cos values given these integer
* indices into table */
/* #define AVMD_FAST_MATH */
/* define/undefine this to classify avmd beep detection as valid
* only when there is required number of consecutive elements
* in the SMA buffer without reset */
#define AVMD_REQUIRE_CONTINUOUS_STREAK
/* define number of samples to skip starting from the beginning
* of frame and after reset */
#define AVMD_SAMLPE_TO_SKIP_N 6
/* define/undefine this to enable/disable simplified estimation
* of frequency based on approximation of sin(x) with (x)
* in the range x=[0,PI/2] */
#define AVMD_SIMPLIFIED_ESTIMATION
/* define/undefine to enable/disable avmd on incoming audio */
#define AVMD_INBOUND_CHANNEL
/* define/undefine to enable/disable avmd on outgoing audio */
/*#define AVMD_OUTBOUND_CHANNEL*/
#endif /* __AVMD_OPTIONS_H__ */

View File

@ -1,9 +1,9 @@
#ifndef __PSI_H__ #ifndef __AVMD_PSI_H__
#define __PSI_H__ #define __AVMD_PSI_H__
#include "buffer.h"
#include "avmd_buffer.h"
#define PSI(b, i) (GET_SAMPLE((b), ((i) + 1))*GET_SAMPLE((b), ((i) + 1))-GET_SAMPLE((b), ((i) + 2))*GET_SAMPLE((b), ((i) + 0))) #define PSI(b, i) (GET_SAMPLE((b), ((i) + 1))*GET_SAMPLE((b), ((i) + 1))-GET_SAMPLE((b), ((i) + 2))*GET_SAMPLE((b), ((i) + 0)))
#endif #endif /* __AVMD_PSI_H__ */

View File

@ -1,5 +1,15 @@
#ifndef __SMA_BUFFER_H__ /*
#define __SMA_BUFFER_H__ * @brief SMA buffer.
*
* @author Eric des Courtis
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
*/
#ifndef __AVMD_SMA_BUFFER_H__
#define __AVMD_SMA_BUFFER_H__
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#ifndef _MSC_VER #ifndef _MSC_VER
@ -7,7 +17,7 @@
#endif #endif
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "buffer.h" #include "avmd_buffer.h"
typedef struct { typedef struct {
size_t len; size_t len;
@ -31,7 +41,8 @@ typedef struct {
#define GET_SMA_SAMPLE(b, p) ((b)->data[(p) % (b)->len]) #define GET_SMA_SAMPLE(b, p) ((b)->data[(p) % (b)->len])
#define SET_SMA_SAMPLE(b, p, v) ((b)->data[(p) % (b)->len] = (v)) #define SET_SMA_SAMPLE(b, p, v) ((b)->data[(p) % (b)->len] = (v))
#define GET_CURRENT_SMA_POS(b) ((b)->lpos) #define GET_CURRENT_SMA_POS(b) ((b)->pos)
#define GET_CURRENT_SMA_LPOS(b) ((b)->lpos)
#define INC_SMA_POS(b) \ #define INC_SMA_POS(b) \
{ \ { \
@ -41,16 +52,19 @@ typedef struct {
#define APPEND_SMA_VAL(b, v) \ #define APPEND_SMA_VAL(b, v) \
{ \ { \
INC_SMA_POS(b); \
(b)->sma -= ((b)->data[(b)->pos] / (BUFF_TYPE)(b)->len); \ (b)->sma -= ((b)->data[(b)->pos] / (BUFF_TYPE)(b)->len); \
(b)->data[(b)->pos] = (v); \ (b)->data[(b)->pos] = (v); \
(b)->sma += ((b)->data[(b)->pos] / (BUFF_TYPE)(b)->len); \ (((b)->lpos) >= ((b)->len)) ? ((b)->sma += ((b)->data[(b)->pos] / (BUFF_TYPE)(b)->len)) : \
((b)->sma = ((((b)->sma)*((b)->pos)) + ((b)->data[(b)->pos])) / ((BUFF_TYPE)(((b)->pos) + 1))) ; \
INC_SMA_POS(b); \
} }
#define RESET_SMA_BUFFER(b) \ #define RESET_SMA_BUFFER(b) \
{ \ { \
(b)->sma = 0.0; \ (b)->sma = 0.0; \
(void)memset((b)->data, 0, sizeof(BUFF_TYPE) * (b)->len); \ (void)memset((b)->data, 0, sizeof(BUFF_TYPE) * (b)->len); \
(b)->pos = 0; \
(b)->lpos = 0; \
} }
/* /*
@ -60,7 +74,11 @@ typedef struct {
}while(0); }while(0);
*/ */
#endif
#endif /* __AVMD_SMA_BUFFER_H__ */
/* /*
int main(void) int main(void)

View File

@ -1,8 +0,0 @@
#ifndef __DESA2_H__
#define __DESA2_H__
#include <math.h>
#include "buffer.h"
extern double desa2(circ_buffer_t *b, size_t i);
#endif

View File

@ -1,136 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#ifndef _MSC_VER
#include <stdint.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef _MSC_VER
#include <sys/mman.h>
#endif
#include <assert.h>
#include <errno.h>
#include <math.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include "fast_acosf.h"
#include "options.h"
#ifdef FASTMATH
#define SIGN_MASK (0x80000000)
#define DATA_MASK (0x07FFFFF8)
#define SIGN_UNPACK_MASK (0x01000000)
#define DATA_UNPACK_MASK (0x00FFFFFF)
#define VARIA_DATA_MASK (0x87FFFFF8)
#define CONST_DATA_MASK (0x38000000)
#define ACOS_TABLE_LENGTH (1 << 25)
#define ACOS_TABLE_FILENAME "/tmp/acos_table.dat"
typedef union {
uint32_t i;
float f;
} float_conv_t;
#ifdef FAST_ACOSF_TESTING
static float strip_float(float f);
#endif
static uint32_t index_from_float(float f);
static float float_from_index(uint32_t d);
static float *acos_table = NULL;
static int acos_fd = -1;
#ifdef FAST_ACOSF_TESTING
static float strip_float(float f)
{
float_conv_t d;
d.i = d.i & (VARIA_DATA_MASK | CONST_DATA_MASK);
return d.i;
}
#endif
extern void compute_table(void)
{
uint32_t i;
float f;
FILE *acos_table_file;
size_t ret;
acos_table_file = fopen(ACOS_TABLE_FILENAME, "w");
for (i = 0; i < ACOS_TABLE_LENGTH; i++) {
f = acosf(float_from_index(i));
ret = fwrite(&f, sizeof(f), 1, acos_table_file);
assert(ret != 0);
}
ret = fclose(acos_table_file);
assert(ret != EOF);
}
extern void init_fast_acosf(void)
{
int ret;
if (acos_table == NULL) {
ret = access(ACOS_TABLE_FILENAME, F_OK);
if (ret == 0) compute_table();
acos_fd = open(ACOS_TABLE_FILENAME, O_RDONLY);
if (acos_fd == -1) perror("Could not open file " ACOS_TABLE_FILENAME);
assert(acos_fd != -1);
acos_table = (float *)mmap(
NULL,
ACOS_TABLE_LENGTH * sizeof(float),
PROT_READ,
MAP_SHARED | MAP_POPULATE,
acos_fd,
0
);
}
}
extern void destroy_fast_acosf(void)
{
int ret;
ret = munmap(acos_table, ACOS_TABLE_LENGTH);
assert(ret != -1);
ret = close(acos_fd);
assert(ret != -1);
acos_table = NULL;
}
extern float fast_acosf(float x)
{
return acos_table[index_from_float(x)];
}
static uint32_t index_from_float(float f)
{
float_conv_t d;
d.f = f;
return ((d.i & SIGN_MASK) >> 7) | ((d.i & DATA_MASK) >> 3);
}
static float float_from_index(uint32_t d)
{
float_conv_t f;
f.i = ((d & SIGN_UNPACK_MASK) << 7) | ((d & DATA_UNPACK_MASK) << 3) | CONST_DATA_MASK;
return f.f;
}
#endif

View File

@ -1,10 +0,0 @@
#ifndef __FAST_ACOSF_H__
#define __FAST_ACOSF_H__
extern void init_fast_acosf(void);
extern float fast_acosf(float x);
extern void destroy_fast_acosf(void);
extern void compute_table(void);
#endif

View File

@ -1,36 +0,0 @@
#ifndef __GOERTZEL_H__
#include <math.h>
#include "goertzel.h"
#include "buffer.h"
/*! \brief Identify frequency components of a signal
* @author Eric des Courtis
* @param b A circular buffer
* @param pos Position in the buffer
* @param f Frequency to look at
* @param num Number of samples to look at
* @return A power estimate for frequency f at position pos in the stream
*/
extern double goertzel(circ_buffer_t *b, size_t pos, double f, size_t num)
{
double s = 0.0;
double p = 0.0;
double p2 = 0.0;
double coeff;
size_t i;
coeff = 2.0 * cos(2.0 * M_PI * f);
for (i = 0; i < num; i++) {
/* TODO: optimize to avoid GET_SAMPLE when possible */
s = GET_SAMPLE(b, i + pos) + (coeff * p) - p2;
p2 = p;
p = s;
}
return (p2 * p2) + (p * p) - (coeff * p2 * p);
}
#endif

View File

@ -1,18 +0,0 @@
#ifndef __GOERTZEL_H__
#define __GOERTZEL_H__
#ifndef _MSC_VER
#include <stdint.h>
#endif
#include "buffer.h"
#if !defined(M_PI)
/* C99 systems may not define M_PI */
#define M_PI 3.14159265358979323846264338327
#endif
extern double goertzel(circ_buffer_t *b, size_t pos, double f, size_t num);
#endif

View File

@ -6,37 +6,37 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="amplitude.h"> <ClInclude Include="avmd_amplitude.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="buffer.h"> <ClInclude Include="avmd_buffer.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="desa2.h"> <ClInclude Include="avmd_desa2.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="fast_acosf.h"> <ClInclude Include="avmd_fast_acosf.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="goertzel.h"> <ClInclude Include="avmd_goertzel.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="options.h"> <ClInclude Include="avmd_options.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="psi.h"> <ClInclude Include="avmd_psi.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="sma_buf.h"> <ClInclude Include="avmd_sma_buf.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="amplitude.c" /> <ClCompile Include="avmd_amplitude.c" />
<ClCompile Include="buffer.c" /> <ClCompile Include="avmd_buffer.c" />
<ClCompile Include="desa2.c" /> <ClCompile Include="avmd_desa2.c" />
<ClCompile Include="fast_acosf.c" /> <ClCompile Include="avmd_fast_acosf.c" />
<ClCompile Include="goertzel.c" /> <ClCompile Include="avmd_goertzel.c" />
<ClCompile Include="mod_avmd.c" /> <ClCompile Include="mod_avmd.c" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -124,21 +124,21 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="amplitude.h" /> <ClInclude Include="avmd_amplitude.h" />
<ClInclude Include="buffer.h" /> <ClInclude Include="avmd_buffer.h" />
<ClInclude Include="desa2.h" /> <ClInclude Include="avmd_desa2.h" />
<ClInclude Include="fast_acosf.h" /> <ClInclude Include="avmd_fast_acosf.h" />
<ClInclude Include="goertzel.h" /> <ClInclude Include="avmd_goertzel.h" />
<ClInclude Include="options.h" /> <ClInclude Include="avmd_options.h" />
<ClInclude Include="psi.h" /> <ClInclude Include="avmd_psi.h" />
<ClInclude Include="sma_buf.h" /> <ClInclude Include="avmd_sma_buf.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="amplitude.c" /> <ClCompile Include="avmd_amplitude.c" />
<ClCompile Include="buffer.c" /> <ClCompile Include="avmd_buffer.c" />
<ClCompile Include="desa2.c" /> <ClCompile Include="avmd_desa2.c" />
<ClCompile Include="fast_acosf.c" /> <ClCompile Include="avmd_fast_acosf.c" />
<ClCompile Include="goertzel.c" /> <ClCompile Include="avmd_goertzel.c" />
<ClCompile Include="mod_avmd.c" /> <ClCompile Include="mod_avmd.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -154,4 +154,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>
</Project> </Project>

View File

@ -25,9 +25,14 @@
* *
* This module detects voicemail beeps using a generalized approach. * This module detects voicemail beeps using a generalized approach.
* *
* Modifications:
* Piotr Gregor <piotrek.gregor gmail.com>:
* FS-8808, FS-8809, FS-8810, FS-8852, FS-8853, FS-8854, FS-8855,
* FS-8860, FS-8861, FS-8875
*/ */
#include <switch.h> #include <switch.h>
#include <g711.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -39,12 +44,23 @@
#define ISNAN(x) (isnan(x)) #define ISNAN(x) (isnan(x))
#endif #endif
#include "avmd_buffer.h"
#include "avmd_desa2_tweaked.h"
#include "avmd_sma_buf.h"
#include "avmd_options.h"
#ifdef AVMD_FAST_MATH
#include "avmd_fast_acosf.h"
#endif
/*! Calculate how many audio samples per ms based on the rate */ /*! Calculate how many audio samples per ms based on the rate */
#define SAMPLES_PER_MS(r, m) ((r) / (1000/(m))) #define SAMPLES_PER_MS(r, m) ((r) / (1000/(m)))
/*! Minimum beep length */ /*! Minimum beep length */
#define BEEP_TIME (100) #define BEEP_TIME (2)
/*! How often to evaluate the output of desa2 in ms */ /*! How often to evaluate the output of desa2 in ms */
#define SINE_TIME (10) #define SINE_TIME (2*0.125)
/*! How long in samples does desa2 results get evaluated */ /*! How long in samples does desa2 results get evaluated */
#define SINE_LEN(r) SAMPLES_PER_MS((r), SINE_TIME) #define SINE_LEN(r) SAMPLES_PER_MS((r), SINE_TIME)
/*! How long in samples is the minimum beep length */ /*! How long in samples is the minimum beep length */
@ -59,23 +75,27 @@
#define TO_HZ(r, f) (((r) * (f)) / (2.0 * M_PI)) #define TO_HZ(r, f) (((r) * (f)) / (2.0 * M_PI))
/*! Minimum beep frequency in Hertz */ /*! Minimum beep frequency in Hertz */
#define MIN_FREQUENCY (300.0) #define MIN_FREQUENCY (300.0)
/*! Minimum frequency as digital normalized frequency */
#define MIN_FREQUENCY_R(r) ((2.0 * M_PI * MIN_FREQUENCY) / (r)) #define MIN_FREQUENCY_R(r) ((2.0 * M_PI * MIN_FREQUENCY) / (r))
/*! Maximum beep frequency in Hertz */ /*!
* Maximum beep frequency in Hertz
* Note: The maximum frequency the DESA-2 algorithm can uniquely
* identify is 0.25 of the sampling rate. All the frequencies
* below that level are detected unambiguously. This means 2kHz
* for 8kHz audio. All the frequencies above 0.25 sampling rate
* will be aliased to some frequency below that threshold.
* This is not a problem here as we are interested in detection
* of any sine wave instead of detection of particular frequency.
*/
#define MAX_FREQUENCY (2500.0) #define MAX_FREQUENCY (2500.0)
/*! Maximum frequency as digital normalized frequency */
#define MAX_FREQUENCY_R(r) ((2.0 * M_PI * MAX_FREQUENCY) / (r)) #define MAX_FREQUENCY_R(r) ((2.0 * M_PI * MAX_FREQUENCY) / (r))
/* decrease this value to eliminate false positives */ /* decrease this value to eliminate false positives */
#define VARIANCE_THRESHOLD (0.001) #define VARIANCE_THRESHOLD (0.00025)
#include "amplitude.h" #ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
#include "buffer.h" /* increase this value to eliminate false positives */
#include "desa2.h" #define SAMPLES_CONSECUTIVE_STREAK 15
//#include "goertzel.h"
#include "psi.h"
#include "sma_buf.h"
#include "options.h"
#ifdef FASTMATH
#include "fast_acosf.h"
#endif #endif
/*! Syntax of the API call. */ /*! Syntax of the API call. */
@ -87,13 +107,16 @@
/*! FreeSWITCH CUSTOM event type. */ /*! FreeSWITCH CUSTOM event type. */
#define AVMD_EVENT_BEEP "avmd::beep" #define AVMD_EVENT_BEEP "avmd::beep"
#define AVMD_CHAR_BUF_LEN 10
#define AVMD_BUF_LINEAR_LEN 160
/* Prototypes */ /* Prototypes */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown); SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown);
SWITCH_STANDARD_API(avmd_api_main); SWITCH_STANDARD_API(avmd_api_main);
SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load); SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load);
SWITCH_MODULE_DEFINITION(mod_avmd, mod_avmd_load, NULL, NULL); SWITCH_MODULE_DEFINITION(mod_avmd, mod_avmd_load, mod_avmd_shutdown, NULL);
SWITCH_STANDARD_APP(avmd_start_function); SWITCH_STANDARD_APP(avmd_start_function);
/*! Status of the beep detection */ /*! Status of the beep detection */
@ -121,11 +144,18 @@ typedef struct {
/* freq_table_t ft; */ /* freq_table_t ft; */
avmd_state_t state; avmd_state_t state;
switch_time_t start_time; switch_time_t start_time;
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
size_t samples_streak; /* number of DESA samples in single streak without reset
needed to validate SMA estimator */
#endif
size_t sample_count;
} avmd_session_t; } avmd_session_t;
static void avmd_process(avmd_session_t *session, switch_frame_t *frame); static void avmd_process(avmd_session_t *session, switch_frame_t *frame);
static switch_bool_t avmd_callback(switch_media_bug_t * bug, void *user_data, switch_abc_type_t type); static switch_bool_t avmd_callback(switch_media_bug_t * bug,
static void init_avmd_session_data(avmd_session_t *avmd_session, switch_core_session_t *fs_session); void *user_data, switch_abc_type_t type);
static void init_avmd_session_data(avmd_session_t *avmd_session,
switch_core_session_t *fs_session);
/*! \brief The avmd session data initialization function. /*! \brief The avmd session data initialization function.
@ -133,17 +163,25 @@ static void init_avmd_session_data(avmd_session_t *avmd_session, switch_core_se
* @param avmd_session A reference to a avmd session. * @param avmd_session A reference to a avmd session.
* @param fs_session A reference to a FreeSWITCH session. * @param fs_session A reference to a FreeSWITCH session.
*/ */
static void init_avmd_session_data(avmd_session_t *avmd_session, switch_core_session_t *fs_session) static void init_avmd_session_data(avmd_session_t *avmd_session,
switch_core_session_t *fs_session)
{ {
/*! This is a worst case sample rate estimate */ /*! This is a worst case sample rate estimate */
avmd_session->rate = 48000; avmd_session->rate = 48000;
INIT_CIRC_BUFFER(&avmd_session->b, (size_t)BEEP_LEN(avmd_session->rate), (size_t)FRAME_LEN(avmd_session->rate), fs_session); INIT_CIRC_BUFFER(&avmd_session->b,
(size_t)BEEP_LEN(avmd_session->rate),
(size_t)FRAME_LEN(avmd_session->rate),
fs_session);
avmd_session->session = fs_session; avmd_session->session = fs_session;
avmd_session->pos = 0; avmd_session->pos = 0;
avmd_session->f = 0.0; avmd_session->f = 0.0;
avmd_session->state.last_beep = 0; avmd_session->state.last_beep = 0;
avmd_session->state.beep_state = BEEP_NOTDETECTED; avmd_session->state.beep_state = BEEP_NOTDETECTED;
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
avmd_session->samples_streak = SAMPLES_CONSECUTIVE_STREAK;
#endif
avmd_session->sample_count = 0;
INIT_SMA_BUFFER( INIT_SMA_BUFFER(
&avmd_session->sma_b, &avmd_session->sma_b,
@ -167,7 +205,8 @@ static void init_avmd_session_data(avmd_session_t *avmd_session, switch_core_se
* @param type The switch callback type. * @param type The switch callback type.
* @return The success or failure of the function. * @return The success or failure of the function.
*/ */
static switch_bool_t avmd_callback(switch_media_bug_t * bug, void *user_data, switch_abc_type_t type) static switch_bool_t avmd_callback(switch_media_bug_t * bug,
void *user_data, switch_abc_type_t type)
{ {
avmd_session_t *avmd_session; avmd_session_t *avmd_session;
switch_codec_t *read_codec; switch_codec_t *read_codec;
@ -185,7 +224,8 @@ static switch_bool_t avmd_callback(switch_media_bug_t * bug, void *user_data, sw
read_codec = switch_core_session_get_read_codec(avmd_session->session); read_codec = switch_core_session_get_read_codec(avmd_session->session);
avmd_session->rate = read_codec->implementation->samples_per_second; avmd_session->rate = read_codec->implementation->samples_per_second;
avmd_session->start_time = switch_micro_time_now(); avmd_session->start_time = switch_micro_time_now();
/* avmd_session->vmd_codec.channels = read_codec->implementation->number_of_channels; */ /* avmd_session->vmd_codec.channels =
* read_codec->implementation->number_of_channels; */
break; break;
case SWITCH_ABC_TYPE_READ_REPLACE: case SWITCH_ABC_TYPE_READ_REPLACE:
@ -194,7 +234,9 @@ static switch_bool_t avmd_callback(switch_media_bug_t * bug, void *user_data, sw
return SWITCH_TRUE; return SWITCH_TRUE;
case SWITCH_ABC_TYPE_WRITE_REPLACE: case SWITCH_ABC_TYPE_WRITE_REPLACE:
break; frame = switch_core_media_bug_get_write_replace_frame(bug);
avmd_process(avmd_session, frame);
return SWITCH_TRUE;
default: default:
break; break;
@ -206,35 +248,92 @@ static switch_bool_t avmd_callback(switch_media_bug_t * bug, void *user_data, sw
/*! \brief FreeSWITCH module loading function. /*! \brief FreeSWITCH module loading function.
* *
* @author Eric des Courtis * @author Eric des Courtis
* @return Load success or failure. * @par Modifications: Piotr Gregor
* @return On success SWITCH_STATUS_SUCCES,
* on failure SWITCH_STATUS_TERM.
*/ */
SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load) SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load)
{ {
#ifdef AVMD_FAST_MATH
char err[150];
int ret;
#endif
switch_application_interface_t *app_interface; switch_application_interface_t *app_interface;
switch_api_interface_t *api_interface; switch_api_interface_t *api_interface;
/* connect my internal structure to the blank pointer passed to me */ /* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname); *module_interface = switch_loadable_module_create_module_interface(pool, modname);
if (switch_event_reserve_subclass(AVMD_EVENT_BEEP) != SWITCH_STATUS_SUCCESS) { if (switch_event_reserve_subclass(AVMD_EVENT_BEEP) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", AVMD_EVENT_BEEP); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
"Couldn't register subclass [%s]!\n", AVMD_EVENT_BEEP);
return SWITCH_STATUS_TERM; return SWITCH_STATUS_TERM;
} }
switch_log_printf( switch_log_printf(
SWITCH_CHANNEL_LOG, SWITCH_CHANNEL_LOG,
SWITCH_LOG_NOTICE, SWITCH_LOG_NOTICE,
"Advanced Voicemail detection enabled\n" "Advanced Voicemail detection enabled\n"
); );
#ifdef FASTMATH #ifdef AVMD_FAST_MATH
init_fast_acosf(); ret = init_fast_acosf();
if (ret != 0) {
strerror_r(errno, err, 150);
switch (ret) {
case -1:
switch_log_printf(
SWITCH_CHANNEL_LOG,
SWITCH_LOG_ERROR,
"Can't access file [%s], error [%s]\n",
ACOS_TABLE_FILENAME, err
);
break;
case -2:
switch_log_printf(
SWITCH_CHANNEL_LOG,
SWITCH_LOG_ERROR,
"Error creating file [%s], error [%s]\n",
ACOS_TABLE_FILENAME, err
);
break;
case -3:
switch_log_printf(
SWITCH_CHANNEL_LOG,
SWITCH_LOG_ERROR,
"Access rights are OK but can't open file [%s], error [%s]\n",
ACOS_TABLE_FILENAME, err
);
break;
case -4:
switch_log_printf(
SWITCH_CHANNEL_LOG,
SWITCH_LOG_ERROR,
"Access rights are OK but can't mmap file [%s], error [%s]\n",
ACOS_TABLE_FILENAME, err
);
break;
default:
switch_log_printf(
SWITCH_CHANNEL_LOG,
SWITCH_LOG_ERROR,
"Unknown error [%d] while initializing fast cos table [%s], "
"errno [%s]\n", ret, ACOS_TABLE_FILENAME, err
);
return SWITCH_STATUS_TERM;
}
return SWITCH_STATUS_TERM;
} else
switch_log_printf( switch_log_printf(
SWITCH_CHANNEL_LOG, SWITCH_CHANNEL_LOG,
SWITCH_LOG_NOTICE, SWITCH_LOG_NOTICE,
"Advanced Voicemail detection: fast math enabled\n" "Advanced Voicemail detection: fast math enabled, arc cosine table "
"is [%s]\n", ACOS_TABLE_FILENAME
); );
#endif #endif
@ -246,9 +345,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load)
avmd_start_function, avmd_start_function,
"[start] [stop]", "[start] [stop]",
SAF_NONE SAF_NONE
); );
SWITCH_ADD_API(api_interface, "avmd", "Voicemail beep detection", avmd_api_main, AVMD_SYNTAX); SWITCH_ADD_API(api_interface, "avmd", "Voicemail beep detection",
avmd_api_main, AVMD_SYNTAX);
/* indicate that the module should continue to be loaded */ /* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
@ -266,6 +366,7 @@ SWITCH_STANDARD_APP(avmd_start_function)
switch_status_t status; switch_status_t status;
switch_channel_t *channel; switch_channel_t *channel;
avmd_session_t *avmd_session; avmd_session_t *avmd_session;
switch_media_bug_flag_t flags = 0;
if (session == NULL) if (session == NULL)
return; return;
@ -293,10 +394,19 @@ SWITCH_STANDARD_APP(avmd_start_function)
return; return;
} }
avmd_session = (avmd_session_t *)switch_core_session_alloc(session, sizeof(avmd_session_t)); avmd_session = (avmd_session_t *)switch_core_session_alloc(
session, sizeof(avmd_session_t));
init_avmd_session_data(avmd_session, session); init_avmd_session_data(avmd_session, session);
#ifdef AVMD_INBOUND_CHANNEL
flags |= SMBF_READ_REPLACE;
#endif
#ifdef AVMD_OUTBOUND_CHANNEL
flags |= SMBF_WRITE_REPLACE;
#endif
switch_assert(flags != 0);
status = switch_core_media_bug_add( status = switch_core_media_bug_add(
session, session,
"avmd", "avmd",
@ -304,7 +414,7 @@ SWITCH_STANDARD_APP(avmd_start_function)
avmd_callback, avmd_callback,
avmd_session, avmd_session,
0, 0,
SMBF_READ_REPLACE, flags,
&bug &bug
); );
@ -328,11 +438,34 @@ SWITCH_STANDARD_APP(avmd_start_function)
*/ */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown) SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown)
{ {
#ifdef AVMD_FAST_MATH
int res;
#endif
switch_event_free_subclass(AVMD_EVENT_BEEP); switch_event_free_subclass(AVMD_EVENT_BEEP);
#ifdef FASTMATH #ifdef AVMD_FAST_MATH
destroy_fast_acosf(); res = destroy_fast_acosf();
if (res != 0) {
switch (res) {
case -1:
switch_log_printf(
SWITCH_CHANNEL_LOG,
SWITCH_LOG_ERROR,
"Failed unmap arc cosine table\n"
);
break;
case -2:
switch_log_printf(
SWITCH_CHANNEL_LOG,
SWITCH_LOG_ERROR,
"Failed closing arc cosine table\n"
);
break;
default:
break;
}
}
#endif #endif
switch_log_printf( switch_log_printf(
@ -363,6 +496,7 @@ SWITCH_STANDARD_API(avmd_api_main)
char *ccmd = NULL; char *ccmd = NULL;
char *uuid; char *uuid;
char *command; char *command;
switch_core_media_flag_t flags = 0;
/* No command? Display usage */ /* No command? Display usage */
if (zstr(cmd)) { if (zstr(cmd)) {
@ -429,10 +563,19 @@ SWITCH_STANDARD_API(avmd_api_main)
/* Allocate memory attached to this FreeSWITCH session for /* Allocate memory attached to this FreeSWITCH session for
* use in the callback routine and to store state information */ * use in the callback routine and to store state information */
avmd_session = (avmd_session_t *) switch_core_session_alloc(fs_session, sizeof(avmd_session_t)); avmd_session = (avmd_session_t *) switch_core_session_alloc(
fs_session, sizeof(avmd_session_t));
init_avmd_session_data(avmd_session, fs_session); init_avmd_session_data(avmd_session, fs_session);
#ifdef AVMD_INBOUND_CHANNEL
flags |= SMBF_READ_REPLACE;
#endif
#ifdef AVMD_OUTBOUND_CHANNEL
flags |= SMBF_WRITE_REPLACE;
#endif
switch_assert(flags != 0);
/* Add a media bug that allows me to intercept the /* Add a media bug that allows me to intercept the
* reading leg of the audio stream */ * reading leg of the audio stream */
status = switch_core_media_bug_add( status = switch_core_media_bug_add(
@ -442,7 +585,7 @@ SWITCH_STANDARD_API(avmd_api_main)
avmd_callback, avmd_callback,
avmd_session, avmd_session,
0, 0,
SMBF_READ_REPLACE, flags,
&bug &bug
); );
@ -478,6 +621,7 @@ end:
/*! \brief Process one frame of data with avmd algorithm. /*! \brief Process one frame of data with avmd algorithm.
* @author Eric des Courtis * @author Eric des Courtis
* @par Modifications: Piotr Gregor
* @param session An avmd session. * @param session An avmd session.
* @param frame An audio frame. * @param frame An audio frame.
*/ */
@ -490,90 +634,177 @@ static void avmd_process(avmd_session_t *session, switch_frame_t *frame)
circ_buffer_t *b; circ_buffer_t *b;
size_t pos; size_t pos;
double f; double omega;
#ifdef AVMD_DEBUG
double f;
#endif
double v; double v;
// double error = 0.0; double sma_digital_freq;
// double success = 0.0;
// double amp = 0.0;
// double s_rate;
// double e_rate;
// double avg_a;
//double sine_len;
uint32_t sine_len_i; uint32_t sine_len_i;
//uint32_t beep_len_i; char buf[AVMD_CHAR_BUF_LEN];
// int valid; int sample_to_skip_n = AVMD_SAMLPE_TO_SKIP_N;
size_t sample_n = 0;
b = &session->b; b = &session->b;
/*! If beep has already been detected skip the CPU heavy stuff */ /* If beep has already been detected skip the CPU heavy stuff */
if (session->state.beep_state == BEEP_DETECTED) return; if (session->state.beep_state == BEEP_DETECTED) return;
/*! Precompute values used heavily in the inner loop */ /* Precompute values used heavily in the inner loop */
sine_len_i = SINE_LEN(session->rate); sine_len_i = SINE_LEN(session->rate);
//sine_len = (double)sine_len_i; //sine_len = (double)sine_len_i;
//beep_len_i = BEEP_LEN(session->rate); //beep_len_i = BEEP_LEN(session->rate);
channel = switch_core_session_get_channel(session->session); channel = switch_core_session_get_channel(session->session);
/*! Insert frame of 16 bit samples into buffer */ /* Insert frame of 16 bit samples into buffer */
INSERT_INT16_FRAME(b, (int16_t *)(frame->data), frame->samples); INSERT_INT16_FRAME(b, (int16_t *)(frame->data), frame->samples);
session->sample_count += frame->samples;
//switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_INFO, "<<< AVMD sine_len_i=%d >>>\n", sine_len_i); /* INNER LOOP -- OPTIMIZATION TARGET */
pos = session->pos;
while (sample_n < (frame->samples - P)) {
/*for (pos = session->pos; pos < (GET_CURRENT_POS(b) - P); pos++) { */
if ((sample_n % sine_len_i) == 0) {
/* Get a desa2 frequency estimate every sine len */
omega = avmd_desa2_tweaked(b, pos + sample_n, session->session);
/*! INNER LOOP -- OPTIMIZATION TARGET */ if (omega < -0.999999 || omega > 0.999999) {
for (pos = session->pos; pos < (GET_CURRENT_POS(b) - P); pos++) { #ifdef AVMD_DEBUG
if ((pos % sine_len_i) == 0) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session),
/*! Get a desa2 frequency estimate every sine len */ SWITCH_LOG_DEBUG, "<<< AVMD RESET >>>\n");
f = desa2(b, pos); #endif
if (f < MIN_FREQUENCY_R(session->rate) || f > MAX_FREQUENCY_R(session->rate)) {
v = 99999.0; v = 99999.0;
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
RESET_SMA_BUFFER(&session->sma_b); RESET_SMA_BUFFER(&session->sma_b);
RESET_SMA_BUFFER(&session->sqa_b); RESET_SMA_BUFFER(&session->sqa_b);
session->samples_streak = SAMPLES_CONSECUTIVE_STREAK;
sample_to_skip_n = AVMD_SAMLPE_TO_SKIP_N;
#endif
} else { } else {
APPEND_SMA_VAL(&session->sma_b, f); if (isnan(omega)) {
APPEND_SMA_VAL(&session->sqa_b, f * f); #ifdef AVMD_DEBUG
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session),
SWITCH_LOG_DEBUG, "<<< AVMD, SKIP NaN >>>\n");
#endif
sample_to_skip_n = AVMD_SAMLPE_TO_SKIP_N;
goto loop_continue;
}
if (session->sma_b.pos > 0 &&
(fabs(omega - session->sma_b.data[session->sma_b.pos - 1]) < 0.00000001)) {
#ifdef AVMD_DEBUG
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG,
"<<< AVMD, SKIP >>>\n");
#endif
goto loop_continue;
}
#ifdef AVMD_DEBUG
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session),
SWITCH_LOG_DEBUG, "<<< AVMD omega [%f] >>>\n", omega);
#endif
if (sample_to_skip_n > 0) {
sample_to_skip_n--;
goto loop_continue;
}
/* calculate variance */ /* saturate */
if (omega < -0.9999)
omega = -0.9999;
if (omega > 0.9999)
omega = 0.9999;
/* append */
APPEND_SMA_VAL(&session->sma_b, omega);
APPEND_SMA_VAL(&session->sqa_b, omega * omega);
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
if (session->samples_streak > 0)
--session->samples_streak;
#endif
/* calculate variance (biased estimator) */
v = session->sqa_b.sma - (session->sma_b.sma * session->sma_b.sma); v = session->sqa_b.sma - (session->sma_b.sma * session->sma_b.sma);
#ifdef AVMD_DEBUG
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG, "<<< AVMD v=%f f=%f %fHz sma=%f sqa=%f >>>\n", v, f, TO_HZ(session->rate, f), session->sma_b.sma, session->sqa_b.sma); #ifdef AVMD_FAST_MATH
f = 0.5 * (double) fast_acosf((float)omega);
sma_digital_freq = 0.5 * (double) fast_acosf((float)session->sma_b.sma);
#else
f = 0.5 * acos(omega);
sma_digital_freq = 0.5 * acos(session->sma_b.sma);
#endif /* AVMD_FAST_MATH */
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG,
"<<< AVMD v[%.10f]\tomega[%f]\tf[%f] [%f]Hz\t\tsma[%f][%f]Hz\t\tsqa[%f]\t"
"streak[%zu] pos[%zu] sample_n[%zu] lpos[%zu] s[%zu]>>>\n",
v, omega, f, TO_HZ(session->rate, f), session->sma_b.sma,
TO_HZ(session->rate, sma_digital_freq), session->sqa_b.sma, session->samples_streak,
session->sma_b.pos, sample_n, session->sma_b.lpos, pos);
#else
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG,
"<<< AVMD v[%.10f]\tomega[%f]\tf[%f] [%f]Hz\t\tsma[%f][%f]Hz\t\tsqa[%f]\tpos[%zu]"
" sample_n[%zu] lpos[%zu] s[%zu]>>>\n", v, omega, f,
TO_HZ(session->rate, f), session->sma_b.sma, TO_HZ(session->rate, sma_digital_freq),
session->sqa_b.sma, session->sma_b.pos, sample_n, session->sma_b.lpos, pos);
#endif /* AVMD_REQUIRE_CONTINUOUS_STREAK */
#endif /* AVMD_DEBUG */
} }
/*! If variance is less than threshold then we have detection */ /* DECISION */
if (v < VARIANCE_THRESHOLD) { /* If variance is less than threshold
* and we have at least two estimates
switch_channel_set_variable_printf(channel, "avmd_total_time", "%d", (int)(switch_micro_time_now() - session->start_time) / 1000); * then we have detection */
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
if (v < VARIANCE_THRESHOLD && (session->sma_b.lpos > 1) && (session->samples_streak == 0)) {
#else
if (v < VARIANCE_THRESHOLD && (session->sma_b.lpos > 1)) {
#endif
#ifdef AVMD_FAST_MATH
sma_digital_freq = 0.5 * (double) fast_acosf((float)session->sma_b.sma);
#else
sma_digital_freq = 0.5 * acos(session->sma_b.sma);
#endif /* AVMD_FAST_MATH */
snprintf(buf, AVMD_CHAR_BUF_LEN, "%f", TO_HZ(session->rate, sma_digital_freq));
switch_channel_set_variable_printf(channel, "avmd_total_time",
"[%d]", (int)(switch_micro_time_now() - session->start_time) / 1000);
switch_channel_execute_on(channel, "execute_on_avmd_beep"); switch_channel_execute_on(channel, "execute_on_avmd_beep");
/*! Throw an event to FreeSWITCH */ /* Throw an event to FreeSWITCH */
status = switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, AVMD_EVENT_BEEP); status = switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, AVMD_EVENT_BEEP);
if (status != SWITCH_STATUS_SUCCESS) return; if (status != SWITCH_STATUS_SUCCESS) return;
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Beep-Status", "stop"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Beep-Status", "stop");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(session->session)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID",
switch_core_session_get_uuid(session->session));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-command", "avmd"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-command", "avmd");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "frequency", buf);
snprintf(buf, AVMD_CHAR_BUF_LEN, "%f", v);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "variance", buf);
if ((switch_event_dup(&event_copy, event)) != SWITCH_STATUS_SUCCESS) return; if ((switch_event_dup(&event_copy, event)) != SWITCH_STATUS_SUCCESS) return;
switch_core_session_queue_event(session->session, &event); switch_core_session_queue_event(session->session, &event);
switch_event_fire(&event_copy); switch_event_fire(&event_copy);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG, "<<< AVMD - Beep Detected >>>\n"); #ifdef AVMD_REPORT_STATUS
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_NOTICE,
"<<< AVMD - Beep Detected: f = [%f], variance = [%f] >>>\n",
TO_HZ(session->rate, sma_digital_freq), v);
#endif
switch_channel_set_variable(channel, "avmd_detect", "TRUE"); switch_channel_set_variable(channel, "avmd_detect", "TRUE");
RESET_SMA_BUFFER(&session->sma_b); RESET_SMA_BUFFER(&session->sma_b);
RESET_SMA_BUFFER(&session->sqa_b); RESET_SMA_BUFFER(&session->sqa_b);
session->state.beep_state = BEEP_DETECTED; session->state.beep_state = BEEP_DETECTED;
return; goto done;
} }
//amp = 0.0;
//success = 0.0;
//error = 0.0;
} }
loop_continue:
++sample_n;
} }
session->pos = pos;
done:
session->pos += sample_n;
session->pos &= b->mask;
return;
} }
/* For Emacs: /* For Emacs:

View File

@ -1,7 +0,0 @@
#ifndef __OPTIONS_H__
#define __OPTIONS_H__
/* #define FASTMATH */
#endif

View File

@ -55,12 +55,14 @@ api_command_t conference_api_sub_commands[] = {
{"auto-3d-position", (void_fn_t) & conference_api_sub_auto_position, CONF_API_SUB_ARGS_SPLIT, "auto-3d-position", "[on|off]"}, {"auto-3d-position", (void_fn_t) & conference_api_sub_auto_position, CONF_API_SUB_ARGS_SPLIT, "auto-3d-position", "[on|off]"},
{"play", (void_fn_t) & conference_api_sub_play, CONF_API_SUB_ARGS_SPLIT, "play", "<file_path> [async|<member_id> [nomux]]"}, {"play", (void_fn_t) & conference_api_sub_play, CONF_API_SUB_ARGS_SPLIT, "play", "<file_path> [async|<member_id> [nomux]]"},
{"pause_play", (void_fn_t) & conference_api_sub_pause_play, CONF_API_SUB_ARGS_SPLIT, "pause", "[<member_id>]"}, {"pause_play", (void_fn_t) & conference_api_sub_pause_play, CONF_API_SUB_ARGS_SPLIT, "pause", "[<member_id>]"},
{"play_status", (void_fn_t) & conference_api_sub_play_status, CONF_API_SUB_ARGS_SPLIT, "play_status", "[<member_id>]"},
{"file_seek", (void_fn_t) & conference_api_sub_file_seek, CONF_API_SUB_ARGS_SPLIT, "file_seek", "[+-]<val> [<member_id>]"}, {"file_seek", (void_fn_t) & conference_api_sub_file_seek, CONF_API_SUB_ARGS_SPLIT, "file_seek", "[+-]<val> [<member_id>]"},
{"say", (void_fn_t) & conference_api_sub_say, CONF_API_SUB_ARGS_AS_ONE, "say", "<text>"}, {"say", (void_fn_t) & conference_api_sub_say, CONF_API_SUB_ARGS_AS_ONE, "say", "<text>"},
{"saymember", (void_fn_t) & conference_api_sub_saymember, CONF_API_SUB_ARGS_AS_ONE, "saymember", "<member_id> <text>"}, {"saymember", (void_fn_t) & conference_api_sub_saymember, CONF_API_SUB_ARGS_AS_ONE, "saymember", "<member_id> <text>"},
{"stop", (void_fn_t) & conference_api_sub_stop, CONF_API_SUB_ARGS_SPLIT, "stop", "<[current|all|async|last]> [<member_id>]"}, {"stop", (void_fn_t) & conference_api_sub_stop, CONF_API_SUB_ARGS_SPLIT, "stop", "<[current|all|async|last]> [<member_id>]"},
{"dtmf", (void_fn_t) & conference_api_sub_dtmf, CONF_API_SUB_MEMBER_TARGET, "dtmf", "<[member_id|all|last|non_moderator]> <digits>"}, {"dtmf", (void_fn_t) & conference_api_sub_dtmf, CONF_API_SUB_MEMBER_TARGET, "dtmf", "<[member_id|all|last|non_moderator]> <digits>"},
{"kick", (void_fn_t) & conference_api_sub_kick, CONF_API_SUB_MEMBER_TARGET, "kick", "<[member_id|all|last|non_moderator]> [<optional sound file>]"}, {"kick", (void_fn_t) & conference_api_sub_kick, CONF_API_SUB_MEMBER_TARGET, "kick", "<[member_id|all|last|non_moderator]> [<optional sound file>]"},
{"vid-flip", (void_fn_t) & conference_api_sub_vid_flip, CONF_API_SUB_MEMBER_TARGET, "vid-flip", "<[member_id|all|last|non_moderator]>"},
{"hup", (void_fn_t) & conference_api_sub_hup, CONF_API_SUB_MEMBER_TARGET, "hup", "<[member_id|all|last|non_moderator]>"}, {"hup", (void_fn_t) & conference_api_sub_hup, CONF_API_SUB_MEMBER_TARGET, "hup", "<[member_id|all|last|non_moderator]>"},
{"mute", (void_fn_t) & conference_api_sub_mute, CONF_API_SUB_MEMBER_TARGET, "mute", "<[member_id|all]|last|non_moderator> [<quiet>]"}, {"mute", (void_fn_t) & conference_api_sub_mute, CONF_API_SUB_MEMBER_TARGET, "mute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
{"tmute", (void_fn_t) & conference_api_sub_tmute, CONF_API_SUB_MEMBER_TARGET, "tmute", "<[member_id|all]|last|non_moderator> [<quiet>]"}, {"tmute", (void_fn_t) & conference_api_sub_tmute, CONF_API_SUB_MEMBER_TARGET, "tmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
@ -135,6 +137,34 @@ switch_status_t conference_api_sub_pause_play(conference_obj_t *conference, swit
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
} }
switch_status_t conference_api_sub_play_status(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
{
if (argc == 2) {
switch_mutex_lock(conference->mutex);
conference_fnode_check_status(conference->fnode, stream);
switch_mutex_unlock(conference->mutex);
return SWITCH_STATUS_SUCCESS;
}
if (argc == 3) {
uint32_t id = atoi(argv[2]);
conference_member_t *member;
if ((member = conference_member_get(conference, id))) {
switch_mutex_lock(member->fnode_mutex);
conference_fnode_check_status(member->fnode, stream);
switch_mutex_unlock(member->fnode_mutex);
switch_thread_rwlock_unlock(member->rwlock);
return SWITCH_STATUS_SUCCESS;
} else {
stream->write_function(stream, "Member: %u not found.\n", id);
}
}
return SWITCH_STATUS_GENERR;
}
/* _In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream */ /* _In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream */
switch_status_t conference_api_main_real(const char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream) switch_status_t conference_api_main_real(const char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream)
{ {
@ -490,11 +520,9 @@ switch_status_t conference_api_sub_unvmute(conference_member_t *member, switch_s
if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) { if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
layer = conference_video_get_layer_locked(member); if ((layer = conference_video_get_layer_locked(member))) {
layer->clear = 1;
if (layer) {
conference_video_clear_layer(layer);
conference_video_release_layer(&layer); conference_video_release_layer(&layer);
} }
@ -628,6 +656,36 @@ switch_status_t conference_api_sub_kick(conference_member_t *member, switch_stre
} }
switch_status_t conference_api_sub_vid_flip(conference_member_t *member, switch_stream_handle_t *stream, void *data)
{
switch_event_t *event;
if (member == NULL) {
return SWITCH_STATUS_GENERR;
}
if (conference_utils_member_test_flag(member, MFLAG_FLIP_VIDEO)) {
conference_utils_member_clear_flag_locked(member, MFLAG_FLIP_VIDEO);
} else {
conference_utils_member_set_flag_locked(member, MFLAG_FLIP_VIDEO);
}
if (stream != NULL) {
stream->write_function(stream, "OK flipped %u\n", member->id);
}
if (member->conference && test_eflag(member->conference, EFLAG_KICK_MEMBER)) {
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
conference_member_add_event_data(member, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "vid-flip-member");
switch_event_fire(&event);
}
}
return SWITCH_STATUS_SUCCESS;
}
switch_status_t conference_api_sub_dtmf(conference_member_t *member, switch_stream_handle_t *stream, void *data) switch_status_t conference_api_sub_dtmf(conference_member_t *member, switch_stream_handle_t *stream, void *data)
{ {
switch_event_t *event; switch_event_t *event;
@ -1163,6 +1221,8 @@ switch_status_t conference_api_sub_write_png(conference_obj_t *conference, switc
switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv) switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
{ {
video_layout_t *vlayout = NULL; video_layout_t *vlayout = NULL;
char *group_name = NULL;
int idx = 0; int idx = 0;
if (!argv[2]) { if (!argv[2]) {
@ -1188,7 +1248,6 @@ switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, swit
if (!strncasecmp(argv[2], "group", 5)) { if (!strncasecmp(argv[2], "group", 5)) {
layout_group_t *lg = NULL; layout_group_t *lg = NULL;
char *group_name = NULL;
int xx = 4; int xx = 4;
if ((group_name = strchr(argv[2], ':'))) { if ((group_name = strchr(argv[2], ':'))) {
@ -1197,7 +1256,7 @@ switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, swit
} else { } else {
group_name = argv[3]; group_name = argv[3];
} }
if (!group_name) { if (!group_name) {
stream->write_function(stream, "Group name not specified.\n"); stream->write_function(stream, "Group name not specified.\n");
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
@ -1208,33 +1267,30 @@ switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, swit
conference->video_layout_group = switch_core_strdup(conference->pool, group_name); conference->video_layout_group = switch_core_strdup(conference->pool, group_name);
conference_utils_set_flag(conference, CFLAG_REFRESH_LAYOUT); conference_utils_set_flag(conference, CFLAG_REFRESH_LAYOUT);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} else {
vlayout = conference_video_find_best_layout(conference, lg, 0);
} }
} } else {
group_name = NULL;
if (!vlayout) {
stream->write_function(stream, "Invalid group layout [%s]\n", group_name);
return SWITCH_STATUS_SUCCESS;
} }
stream->write_function(stream, "Change to layout group [%s]\n", group_name); stream->write_function(stream, "Change to layout group [%s]\n", group_name);
conference->video_layout_group = switch_core_strdup(conference->pool, group_name);
if (argv[xx]) { if (argv[xx]) {
idx = atoi(argv[xx]); if ((idx = atoi(argv[xx])) > 0) {
idx--;
}
} }
} }
} }
if (!vlayout && (vlayout = switch_core_hash_find(conference->layout_hash, argv[2]))) { if (!vlayout && (vlayout = switch_core_hash_find(conference->layout_hash, argv[2]))) {
conference->video_layout_group = NULL;
if (argv[3]) { if (argv[3]) {
idx = atoi(argv[3]); if ((idx = atoi(argv[3]))) {
idx--;
}
} }
} }
if (!vlayout) { if (!vlayout && !group_name) {
stream->write_function(stream, "Invalid layout [%s]\n", argv[2]); stream->write_function(stream, "Invalid layout [%s]\n", argv[2]);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
@ -1248,9 +1304,15 @@ switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, swit
conference->new_personal_vlayout = vlayout; conference->new_personal_vlayout = vlayout;
switch_mutex_unlock(conference->member_mutex); switch_mutex_unlock(conference->member_mutex);
} else { } else {
stream->write_function(stream, "Change canvas %d to layout [%s]\n", idx + 1, vlayout->name);
switch_mutex_lock(conference->canvases[idx]->mutex); switch_mutex_lock(conference->canvases[idx]->mutex);
conference->canvases[idx]->new_vlayout = vlayout; if (vlayout) {
stream->write_function(stream, "Change canvas %d to layout [%s]\n", idx + 1, vlayout->name);
conference->canvases[idx]->new_vlayout = vlayout;
} else if (group_name) {
conference->canvases[idx]->video_layout_group = switch_core_strdup(conference->pool, group_name);
conference_utils_set_flag(conference, CFLAG_REFRESH_LAYOUT);
}
switch_mutex_unlock(conference->canvases[idx]->mutex); switch_mutex_unlock(conference->canvases[idx]->mutex);
} }
@ -1589,6 +1651,24 @@ switch_status_t conference_api_sub_get_uuid(conference_member_t *member, switch_
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
static void clear_res_id(conference_obj_t *conference, conference_member_t *member, const char *id)
{
conference_member_t *imember;
switch_mutex_lock(conference->member_mutex);
for (imember = conference->members; imember; imember = imember->next) {
if (imember == member) {
continue;
}
if (imember->video_reservation_id && !strcasecmp(imember->video_reservation_id, id)) {
imember->video_reservation_id = NULL;
conference_video_detach_video_layer(imember);
}
}
switch_mutex_unlock(conference->member_mutex);
}
switch_status_t conference_api_sub_vid_res_id(conference_member_t *member, switch_stream_handle_t *stream, void *data) switch_status_t conference_api_sub_vid_res_id(conference_member_t *member, switch_stream_handle_t *stream, void *data)
{ {
char *text = (char *) data; char *text = (char *) data;
@ -1608,12 +1688,18 @@ switch_status_t conference_api_sub_vid_res_id(conference_member_t *member, switc
if (zstr(text) || !strcasecmp(text, "clear") || (member->video_reservation_id && !strcasecmp(text, member->video_reservation_id))) { if (zstr(text) || !strcasecmp(text, "clear") || (member->video_reservation_id && !strcasecmp(text, member->video_reservation_id))) {
member->video_reservation_id = NULL; member->video_reservation_id = NULL;
stream->write_function(stream, "+OK reservation_id cleared\n"); stream->write_function(stream, "+OK reservation_id cleared\n");
conference_video_detach_video_layer(member);
} else { } else {
member->video_reservation_id = switch_core_strdup(member->pool, text); clear_res_id(member->conference, member, text);
if (!member->video_reservation_id || strcmp(member->video_reservation_id, text)) {
member->video_reservation_id = switch_core_strdup(member->pool, text);
}
stream->write_function(stream, "+OK reservation_id %s\n", text); stream->write_function(stream, "+OK reservation_id %s\n", text);
conference_video_detach_video_layer(member); conference_video_detach_video_layer(member);
} }
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }

View File

@ -736,6 +736,10 @@ switch_status_t conference_member_add(conference_obj_t *conference, conference_m
channel = switch_core_session_get_channel(member->session); channel = switch_core_session_get_channel(member->session);
if (switch_true(switch_channel_get_variable_dup(member->channel, "video_second_screen", SWITCH_FALSE, -1))) {
conference_utils_member_set_flag(member, MFLAG_SECOND_SCREEN);
}
conference_video_check_avatar(member, SWITCH_FALSE); conference_video_check_avatar(member, SWITCH_FALSE);
if ((var = switch_channel_get_variable_dup(member->channel, "video_initial_canvas", SWITCH_FALSE, -1))) { if ((var = switch_channel_get_variable_dup(member->channel, "video_initial_canvas", SWITCH_FALSE, -1))) {
@ -748,16 +752,16 @@ switch_status_t conference_member_add(conference_obj_t *conference, conference_m
if ((var = switch_channel_get_variable_dup(member->channel, "video_initial_watching_canvas", SWITCH_FALSE, -1))) { if ((var = switch_channel_get_variable_dup(member->channel, "video_initial_watching_canvas", SWITCH_FALSE, -1))) {
uint32_t id = atoi(var) - 1; uint32_t id = atoi(var) - 1;
if (id == 0) { if (id == 0) {
id = conference->canvas_count; id = conference->canvas_count;
} }
if (id <= conference->canvas_count && conference->canvases[id]) { if (id <= conference->canvas_count && conference->canvases[id]) {
member->watching_canvas_id = id; member->watching_canvas_id = id;
} }
} }
conference_video_reset_member_codec_index(member); conference_video_reset_member_codec_index(member);
if (has_video) { if (has_video) {

View File

@ -74,6 +74,13 @@ const char *conference_utils_combine_flag_var(switch_core_session_t *session, co
ret = switch_core_session_sprintf(session, "%s|%s", ret, val); ret = switch_core_session_sprintf(session, "%s|%s", ret, val);
} }
} }
} else if (!strncasecmp(var, var_name, strlen(var_name)) && switch_true(val)) {
char *p = var + strlen(var_name);
if (*p == '_' && *(p+1)) {
p++;
ret = switch_core_session_sprintf(session, "%s|%s", ret, p);
}
} }
} }
@ -129,6 +136,8 @@ void conference_utils_set_mflags(const char *flags, member_flag_t *f)
f[MFLAG_GHOST] = 1; f[MFLAG_GHOST] = 1;
} else if (!strcasecmp(argv[i], "join-only")) { } else if (!strcasecmp(argv[i], "join-only")) {
f[MFLAG_JOIN_ONLY] = 1; f[MFLAG_JOIN_ONLY] = 1;
} else if (!strcasecmp(argv[i], "flip-video")) {
f[MFLAG_FLIP_VIDEO] = 1;
} else if (!strcasecmp(argv[i], "positional")) { } else if (!strcasecmp(argv[i], "positional")) {
f[MFLAG_POSITIONAL] = 1; f[MFLAG_POSITIONAL] = 1;
} else if (!strcasecmp(argv[i], "no-positional")) { } else if (!strcasecmp(argv[i], "no-positional")) {
@ -338,12 +347,18 @@ switch_bool_t conference_utils_test_mflag(conference_obj_t *conference, member_f
void conference_utils_member_set_flag(conference_member_t *member, member_flag_t flag) void conference_utils_member_set_flag(conference_member_t *member, member_flag_t flag)
{ {
member->flags[flag] = 1; member->flags[flag] = 1;
if (flag == MFLAG_SECOND_SCREEN) {
member->flags[MFLAG_CAN_SPEAK] = 0;
member->flags[MFLAG_CAN_HEAR] = 0;
member->flags[MFLAG_CAN_BE_SEEN] = 0;
}
} }
void conference_utils_member_set_flag_locked(conference_member_t *member, member_flag_t flag) void conference_utils_member_set_flag_locked(conference_member_t *member, member_flag_t flag)
{ {
switch_mutex_lock(member->flag_mutex); switch_mutex_lock(member->flag_mutex);
member->flags[flag] = 1; conference_utils_member_set_flag(member, flag);
switch_mutex_unlock(member->flag_mutex); switch_mutex_unlock(member->flag_mutex);
} }

View File

@ -375,6 +375,11 @@ void conference_video_scale_and_patch(mcu_layer_t *layer, switch_image_t *ximg,
return; return;
} }
if (layer->clear) {
conference_video_clear_layer(layer);
layer->clear = 0;
}
if (layer->refresh) { if (layer->refresh) {
switch_img_fill(layer->canvas->img, layer->x_pos, layer->y_pos, layer->screen_w, layer->screen_h, &layer->canvas->letterbox_bgcolor); switch_img_fill(layer->canvas->img, layer->x_pos, layer->y_pos, layer->screen_w, layer->screen_h, &layer->canvas->letterbox_bgcolor);
layer->refresh = 0; layer->refresh = 0;
@ -551,14 +556,16 @@ mcu_layer_t *conference_video_get_layer_locked(conference_member_t *member)
mcu_layer_t *layer = NULL; mcu_layer_t *layer = NULL;
mcu_canvas_t *canvas = NULL; mcu_canvas_t *canvas = NULL;
if (!member || member->canvas_id < 0 || member->video_layer_id < 0) return NULL;
if ((canvas = conference_video_get_canvas_locked(member))) { if ((canvas = conference_video_get_canvas_locked(member))) {
switch_mutex_lock(canvas->mutex); switch_mutex_lock(canvas->mutex);
layer = &canvas->layers[member->video_layer_id];
if (member->video_layer_id > -1) {
layer = &canvas->layers[member->video_layer_id];
}
if (!layer) { if (!layer) {
switch_mutex_unlock(canvas->mutex); switch_mutex_unlock(canvas->mutex);
conference_video_release_canvas(&canvas);
} }
} }
@ -585,10 +592,11 @@ mcu_canvas_t *conference_video_get_canvas_locked(conference_member_t *member)
{ {
mcu_canvas_t *canvas = NULL; mcu_canvas_t *canvas = NULL;
if (!member || member->canvas_id < 0 || member->video_layer_id < 0) return NULL;
switch_mutex_lock(member->conference->canvas_mutex); switch_mutex_lock(member->conference->canvas_mutex);
canvas = member->conference->canvases[member->canvas_id];
if (member->canvas_id > -1 && member->video_layer_id > -1) {
canvas = member->conference->canvases[member->canvas_id];
}
if (!canvas) { if (!canvas) {
switch_mutex_unlock(member->conference->canvas_mutex); switch_mutex_unlock(member->conference->canvas_mutex);
@ -1225,6 +1233,7 @@ void conference_video_write_canvas_image_to_codec_group(conference_obj_t *confer
conference_member_t *imember; conference_member_t *imember;
switch_frame_t write_frame = { 0 }, *frame = NULL; switch_frame_t write_frame = { 0 }, *frame = NULL;
switch_status_t encode_status = SWITCH_STATUS_FALSE; switch_status_t encode_status = SWITCH_STATUS_FALSE;
switch_image_t *scaled_img = codec_set->scaled_img;
write_frame = codec_set->frame; write_frame = codec_set->frame;
frame = &write_frame; frame = &write_frame;
@ -1246,6 +1255,16 @@ void conference_video_write_canvas_image_to_codec_group(conference_obj_t *confer
switch_core_codec_control(&codec_set->codec, SCC_VIDEO_GEN_KEYFRAME, SCCT_NONE, NULL, SCCT_NONE, NULL, NULL, NULL); switch_core_codec_control(&codec_set->codec, SCC_VIDEO_GEN_KEYFRAME, SCCT_NONE, NULL, SCCT_NONE, NULL, NULL, NULL);
} }
if (scaled_img) {
if (!send_keyframe && codec_set->fps_divisor > 1 && (codec_set->frame_count++) % codec_set->fps_divisor) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Skip one frame, total: %d\n", codec_set->frame_count);
return;
}
switch_img_scale(frame->img, &scaled_img, scaled_img->d_w, scaled_img->d_h);
frame->img = scaled_img;
}
do { do {
frame->data = ((unsigned char *)frame->packet) + 12; frame->data = ((unsigned char *)frame->packet) + 12;
@ -1321,7 +1340,13 @@ video_layout_t *conference_video_find_best_layout(conference_obj_t *conference,
{ {
video_layout_node_t *vlnode = NULL, *last = NULL; video_layout_node_t *vlnode = NULL, *last = NULL;
if (!count) count = conference->members_with_video + conference->members_with_avatar; if (!count) {
count = conference->members_with_video;
if (!conference_utils_test_flag(conference, CFLAG_VIDEO_REQUIRED_FOR_CANVAS)) {
count += conference->members_with_avatar;
}
}
for (vlnode = lg->layouts; vlnode; vlnode = vlnode->next) { for (vlnode = lg->layouts; vlnode; vlnode = vlnode->next) {
if (vlnode->vlayout->layers >= (int)count) { if (vlnode->vlayout->layers >= (int)count) {
@ -1356,21 +1381,22 @@ void conference_video_vmute_snap(conference_member_t *member, switch_bool_t clea
if (member->canvas_id > -1 && member->video_layer_id > -1) { if (member->canvas_id > -1 && member->video_layer_id > -1) {
mcu_layer_t *layer = NULL; mcu_layer_t *layer = NULL;
mcu_canvas_t *canvas = NULL; mcu_canvas_t *canvas = NULL;
canvas = conference_video_get_canvas_locked(member);
switch_mutex_lock(canvas->mutex); if ((canvas = conference_video_get_canvas_locked(member))) {
layer = &canvas->layers[member->video_layer_id];
switch_img_free(&layer->mute_img); switch_mutex_lock(canvas->mutex);
switch_img_free(&member->video_mute_img); layer = &canvas->layers[member->video_layer_id];
switch_img_free(&layer->mute_img);
switch_img_free(&member->video_mute_img);
if (!clear && layer->cur_img) { if (!clear && layer->cur_img) {
switch_img_copy(layer->cur_img, &member->video_mute_img); switch_img_copy(layer->cur_img, &member->video_mute_img);
switch_img_copy(layer->cur_img, &layer->mute_img); switch_img_copy(layer->cur_img, &layer->mute_img);
}
switch_mutex_unlock(canvas->mutex);
conference_video_release_canvas(&canvas);
} }
switch_mutex_unlock(canvas->mutex);
conference_video_release_canvas(&canvas);
} }
} }
@ -1539,18 +1565,17 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_write_thread_run(switch_thread_
canvas = member->conference->canvases[member->canvas_id]; canvas = member->conference->canvases[member->canvas_id];
layer = &canvas->layers[member->video_layer_id]; layer = &canvas->layers[member->video_layer_id];
if (!layer->need_patch || switch_thread_rwlock_tryrdlock(canvas->video_rwlock) != SWITCH_STATUS_SUCCESS) { if (layer->need_patch && switch_thread_rwlock_tryrdlock(canvas->video_rwlock) == SWITCH_STATUS_SUCCESS) {
canvas = NULL; if (layer->need_patch) {
layer = NULL; conference_video_scale_and_patch(layer, NULL, SWITCH_FALSE);
layer->need_patch = 0;
}
switch_thread_rwlock_unlock(canvas->video_rwlock);
} }
} }
switch_mutex_unlock(member->conference->canvas_mutex); switch_mutex_unlock(member->conference->canvas_mutex);
if (canvas && layer && layer->need_patch) {
conference_video_scale_and_patch(layer, NULL, SWITCH_FALSE);
layer->need_patch = 0;
switch_thread_rwlock_unlock(canvas->video_rwlock);
}
} }
} }
@ -1605,6 +1630,10 @@ void conference_video_check_avatar(conference_member_t *member, switch_bool_t fo
return; return;
} }
if (conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) {
return;
}
canvas = conference_video_get_canvas_locked(member); canvas = conference_video_get_canvas_locked(member);
if (conference_utils_test_flag(member->conference, CFLAG_VIDEO_REQUIRED_FOR_CANVAS) && if (conference_utils_test_flag(member->conference, CFLAG_VIDEO_REQUIRED_FOR_CANVAS) &&
@ -2130,6 +2159,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
int last_personal = conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS) ? 1 : 0; int last_personal = conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS) ? 1 : 0;
canvas->video_timer_reset = 1; canvas->video_timer_reset = 1;
canvas->video_layout_group = conference->video_layout_group;
packet = switch_core_alloc(conference->pool, SWITCH_RTP_MAX_BUF_LEN); packet = switch_core_alloc(conference->pool, SWITCH_RTP_MAX_BUF_LEN);
@ -2142,13 +2172,13 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
switch_image_t *async_file_img = NULL, *normal_file_img = NULL, *file_imgs[2] = { 0 }; switch_image_t *async_file_img = NULL, *normal_file_img = NULL, *file_imgs[2] = { 0 };
switch_frame_t file_frame = { 0 }; switch_frame_t file_frame = { 0 };
int j = 0, personal = conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS) ? 1 : 0; int j = 0, personal = conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS) ? 1 : 0;
int video_count = 0;
if (!personal) { if (!personal) {
switch_mutex_lock(canvas->mutex); if (canvas->new_vlayout && switch_mutex_trylock(conference->canvas_mutex) == SWITCH_STATUS_SUCCESS) {
if (canvas->new_vlayout) {
conference_video_init_canvas_layers(conference, canvas, NULL); conference_video_init_canvas_layers(conference, canvas, NULL);
switch_mutex_unlock(conference->canvas_mutex);
} }
switch_mutex_unlock(canvas->mutex);
} }
if (canvas->video_timer_reset) { if (canvas->video_timer_reset) {
@ -2162,7 +2192,24 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
canvas->send_keyframe = 1; canvas->send_keyframe = 1;
} }
video_count = 0;
switch_mutex_lock(conference->member_mutex);
for (imember = conference->members; imember; imember = imember->next) {
int no_muted = conference_utils_test_flag(imember->conference, CFLAG_VIDEO_MUTE_EXIT_CANVAS);
int no_av = conference_utils_test_flag(imember->conference, CFLAG_VIDEO_REQUIRED_FOR_CANVAS);
int seen = conference_utils_member_test_flag(imember, MFLAG_CAN_BE_SEEN);
if (imember->channel && switch_channel_ready(imember->channel) && switch_channel_test_flag(imember->channel, CF_VIDEO_READY) &&
!conference_utils_member_test_flag(imember, MFLAG_SECOND_SCREEN) &&
conference_utils_member_test_flag(imember, MFLAG_RUNNING) && (!no_muted || seen) && (!no_av || imember->avatar_png_img)
&& imember->canvas_id == canvas->canvas_id && imember->video_media_flow != SWITCH_MEDIA_FLOW_SENDONLY) {
video_count++;
}
}
canvas->video_count = video_count;
switch_mutex_unlock(conference->member_mutex);
switch_core_timer_next(&canvas->timer); switch_core_timer_next(&canvas->timer);
now = switch_micro_time_now(); now = switch_micro_time_now();
@ -2186,28 +2233,18 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
if (members_with_avatar != conference->members_with_avatar) { if (members_with_avatar != conference->members_with_avatar) {
count_changed = 1; count_changed = 1;
} }
if (conference_utils_test_flag(conference, CFLAG_REFRESH_LAYOUT)) {
count_changed = 1;
conference_utils_clear_flag(conference, CFLAG_REFRESH_LAYOUT);
}
if (count_changed && !personal) { if (count_changed && !personal) {
layout_group_t *lg = NULL; layout_group_t *lg = NULL;
video_layout_t *vlayout = NULL; video_layout_t *vlayout = NULL;
int canvas_count = 0;
switch_mutex_lock(conference->member_mutex);
for (imember = conference->members; imember; imember = imember->next) {
int no_muted = conference_utils_test_flag(imember->conference, CFLAG_VIDEO_MUTE_EXIT_CANVAS);
int no_av = conference_utils_test_flag(imember->conference, CFLAG_VIDEO_REQUIRED_FOR_CANVAS);
int seen = conference_utils_member_test_flag(imember, MFLAG_CAN_BE_SEEN);
if (imember->channel && switch_channel_ready(imember->channel) && switch_channel_test_flag(imember->channel, CF_VIDEO_READY) && if (canvas->video_layout_group && (lg = switch_core_hash_find(conference->layout_group_hash, canvas->video_layout_group))) {
conference_utils_member_test_flag(imember, MFLAG_RUNNING) && (!no_muted || seen) && (!no_av || imember->avatar_png_img) if ((vlayout = conference_video_find_best_layout(conference, lg, canvas->video_count))) {
&& imember->canvas_id == canvas->canvas_id && imember->video_media_flow != SWITCH_MEDIA_FLOW_SENDONLY) {
canvas_count++;
}
}
switch_mutex_unlock(conference->member_mutex);
if (conference->video_layout_group && (lg = switch_core_hash_find(conference->layout_group_hash, conference->video_layout_group))) {
if ((vlayout = conference_video_find_best_layout(conference, lg, canvas_count))) {
switch_mutex_lock(conference->member_mutex); switch_mutex_lock(conference->member_mutex);
canvas->new_vlayout = vlayout; canvas->new_vlayout = vlayout;
switch_mutex_unlock(conference->member_mutex); switch_mutex_unlock(conference->member_mutex);
@ -2313,6 +2350,28 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
write_codecs[i]->frame.data = ((uint8_t *)write_codecs[i]->frame.packet) + 12; write_codecs[i]->frame.data = ((uint8_t *)write_codecs[i]->frame.packet) + 12;
write_codecs[i]->frame.packetlen = buflen; write_codecs[i]->frame.packetlen = buflen;
write_codecs[i]->frame.buflen = buflen - 12; write_codecs[i]->frame.buflen = buflen - 12;
if (conference->scale_h264_canvas_width > 0 && conference->scale_h264_canvas_height > 0 && !strcmp(check_codec->implementation->iananame, "H264")) {
int32_t bw = -1;
write_codecs[i]->fps_divisor = conference->scale_h264_canvas_fps_divisor;
write_codecs[i]->scaled_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, conference->scale_h264_canvas_width, conference->scale_h264_canvas_height, 16);
if (conference->scale_h264_canvas_bandwidth) {
if (strcasecmp(conference->scale_h264_canvas_bandwidth, "auto")) {
bw = switch_parse_bandwidth_string(conference->scale_h264_canvas_bandwidth);
}
}
if (bw == -1) {
float fps = conference->video_fps.fps;
if (write_codecs[i]->fps_divisor) fps /= write_codecs[i]->fps_divisor;
bw = switch_calc_bitrate(conference->scale_h264_canvas_width, conference->scale_h264_canvas_height, conference->video_quality, fps);
}
switch_core_codec_control(&write_codecs[i]->codec, SCC_VIDEO_BANDWIDTH, SCCT_INT, &bw, SCCT_NONE, NULL, NULL, NULL);
}
switch_set_flag((&write_codecs[i]->frame), SFF_RAW_RTP); switch_set_flag((&write_codecs[i]->frame), SFF_RAW_RTP);
} }
@ -2348,7 +2407,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
} }
//VIDFLOOR //VIDFLOOR
if (canvas->layout_floor_id > -1 && imember->id == conference->video_floor_holder && if (conference->canvas_count == 1 && canvas->layout_floor_id > -1 && imember->id == conference->video_floor_holder &&
imember->video_layer_id != canvas->layout_floor_id) { imember->video_layer_id != canvas->layout_floor_id) {
conference_video_attach_video_layer(imember, canvas, canvas->layout_floor_id); conference_video_attach_video_layer(imember, canvas, canvas->layout_floor_id);
} }
@ -2494,11 +2553,6 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
switch_mutex_lock(conference->member_mutex); switch_mutex_lock(conference->member_mutex);
if (conference_utils_test_flag(conference, CFLAG_REFRESH_LAYOUT)) {
count_changed = 1;
conference_utils_clear_flag(conference, CFLAG_REFRESH_LAYOUT);
}
for (imember = conference->members; imember; imember = imember->next) { for (imember = conference->members; imember; imember = imember->next) {
if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO_READY) || if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO_READY) ||
@ -2506,17 +2560,19 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
continue; continue;
} }
if (!imember->canvas) {
if ((vlayout = conference_video_get_layout(conference, conference->video_layout_name, canvas->video_layout_group))) {
conference_video_init_canvas(conference, vlayout, &imember->canvas);
conference_video_init_canvas_layers(conference, imember->canvas, vlayout);
} else {
continue;
}
}
if (conference->new_personal_vlayout) { if (conference->new_personal_vlayout) {
conference_video_init_canvas_layers(conference, imember->canvas, conference->new_personal_vlayout); conference_video_init_canvas_layers(conference, imember->canvas, conference->new_personal_vlayout);
layout_applied++; layout_applied++;
} }
if (!imember->canvas) {
if ((vlayout = conference_video_get_layout(conference, conference->video_layout_name, conference->video_layout_group))) {
conference_video_init_canvas(conference, vlayout, &imember->canvas);
conference_video_init_canvas_layers(conference, imember->canvas, vlayout);
}
}
if (switch_channel_test_flag(imember->channel, CF_VIDEO_REFRESH_REQ)) { if (switch_channel_test_flag(imember->channel, CF_VIDEO_REFRESH_REQ)) {
switch_channel_clear_flag(imember->channel, CF_VIDEO_REFRESH_REQ); switch_channel_clear_flag(imember->channel, CF_VIDEO_REFRESH_REQ);
@ -2542,7 +2598,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
total = 0; total = 0;
} }
if (conference->video_layout_group && (lg = switch_core_hash_find(conference->layout_group_hash, conference->video_layout_group))) { if (canvas->video_layout_group && (lg = switch_core_hash_find(conference->layout_group_hash, canvas->video_layout_group))) {
if ((vlayout = conference_video_find_best_layout(conference, lg, total + file_count))) { if ((vlayout = conference_video_find_best_layout(conference, lg, total + file_count))) {
conference_video_init_canvas_layers(conference, imember->canvas, vlayout); conference_video_init_canvas_layers(conference, imember->canvas, vlayout);
} }
@ -2598,7 +2654,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
int i = 0; int i = 0;
mcu_layer_t *floor_layer = NULL; mcu_layer_t *floor_layer = NULL;
if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO) || if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO) || !imember->canvas ||
(switch_core_session_media_flow(imember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) || (switch_core_session_media_flow(imember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) ||
(switch_core_session_read_lock(imember->session) != SWITCH_STATUS_SUCCESS)) { (switch_core_session_read_lock(imember->session) != SWITCH_STATUS_SUCCESS)) {
continue; continue;
@ -2669,6 +2725,10 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
use_img = omember->pcanvas_img; use_img = omember->pcanvas_img;
if (files_playing && layer && layer == &imember->canvas->layers[imember->canvas->layout_floor_id]) {
use_img = NULL;
}
if (layer) { if (layer) {
if (use_img && !omember->avatar_png_img) { if (use_img && !omember->avatar_png_img) {
@ -2977,6 +3037,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
for (i = 0; i < MAX_MUX_CODECS; i++) { for (i = 0; i < MAX_MUX_CODECS; i++) {
if (write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec)) { if (write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec)) {
switch_core_codec_destroy(&write_codecs[i]->codec); switch_core_codec_destroy(&write_codecs[i]->codec);
switch_img_free(&(write_codecs[i]->scaled_img));
} }
} }
@ -3403,6 +3464,10 @@ void conference_video_set_floor_holder(conference_obj_t *conference, conference_
conference_utils_clear_flag(conference, CFLAG_VID_FLOOR_LOCK); conference_utils_clear_flag(conference, CFLAG_VID_FLOOR_LOCK);
} }
if (conference->canvas_count > 1) {
return;
}
if (member && member->video_reservation_id) { if (member && member->video_reservation_id) {
/* no video floor when a reservation id is set */ /* no video floor when a reservation id is set */
return; return;
@ -3664,6 +3729,11 @@ switch_status_t conference_video_thread_callback(switch_core_session_t *session,
switch_queue_size(member->video_queue) < member->conference->video_fps.fps * 2 && switch_queue_size(member->video_queue) < member->conference->video_fps.fps * 2 &&
!member->conference->playing_video_file) { !member->conference->playing_video_file) {
switch_img_copy(frame->img, &img_copy); switch_img_copy(frame->img, &img_copy);
if (conference_utils_member_test_flag(member, MFLAG_FLIP_VIDEO)) {
switch_img_flip(img_copy);
}
if (switch_queue_trypush(member->video_queue, img_copy) != SWITCH_STATUS_SUCCESS) { if (switch_queue_trypush(member->video_queue, img_copy) != SWITCH_STATUS_SUCCESS) {
switch_img_free(&img_copy); switch_img_free(&img_copy);
} }

View File

@ -279,7 +279,12 @@ void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, void *ob
} }
} }
if (switch_channel_ready(channel) && switch_channel_test_flag(channel, CF_VIDEO_READY) && imember->video_media_flow != SWITCH_MEDIA_FLOW_SENDONLY && (!conference_utils_test_flag(conference, CFLAG_VIDEO_MUTE_EXIT_CANVAS) || conference_utils_member_test_flag(imember, MFLAG_CAN_BE_SEEN))) { if (switch_channel_ready(channel) &&
switch_channel_test_flag(channel, CF_VIDEO_READY) &&
imember->video_media_flow != SWITCH_MEDIA_FLOW_SENDONLY &&
!conference_utils_member_test_flag(imember, MFLAG_SECOND_SCREEN) &&
(!conference_utils_test_flag(conference, CFLAG_VIDEO_MUTE_EXIT_CANVAS) ||
conference_utils_member_test_flag(imember, MFLAG_CAN_BE_SEEN))) {
members_with_video++; members_with_video++;
} }
@ -1249,6 +1254,7 @@ void conference_xlist(conference_obj_t *conference, switch_xml_t x_conference, i
void conference_fnode_toggle_pause(conference_file_node_t *fnode, switch_stream_handle_t *stream) void conference_fnode_toggle_pause(conference_file_node_t *fnode, switch_stream_handle_t *stream)
{ {
if (fnode) { if (fnode) {
switch_core_file_command(&fnode->fh, SCFC_PAUSE_READ);
if (switch_test_flag(fnode, NFLAG_PAUSE)) { if (switch_test_flag(fnode, NFLAG_PAUSE)) {
stream->write_function(stream, "+OK Resume\n"); stream->write_function(stream, "+OK Resume\n");
switch_clear_flag(fnode, NFLAG_PAUSE); switch_clear_flag(fnode, NFLAG_PAUSE);
@ -1259,6 +1265,15 @@ void conference_fnode_toggle_pause(conference_file_node_t *fnode, switch_stream_
} }
} }
void conference_fnode_check_status(conference_file_node_t *fnode, switch_stream_handle_t *stream)
{
if (fnode) {
stream->write_function(stream, "+OK %"SWITCH_INT64_T_FMT "/%" SWITCH_INT64_T_FMT " %s\n",
fnode->fh.vpos, fnode->fh.duration, fnode->fh.file_path);
} else {
stream->write_function(stream, "-ERR Nothing is playing\n");
}
}
void conference_fnode_seek(conference_file_node_t *fnode, switch_stream_handle_t *stream, char *arg) void conference_fnode_seek(conference_file_node_t *fnode, switch_stream_handle_t *stream, char *arg)
{ {
@ -2411,7 +2426,12 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co
const char *force_rate = NULL, *force_interval = NULL, *force_channels = NULL, *presence_id = NULL; const char *force_rate = NULL, *force_interval = NULL, *force_channels = NULL, *presence_id = NULL;
uint32_t force_rate_i = 0, force_interval_i = 0, force_channels_i = 0, video_auto_floor_msec = 0; uint32_t force_rate_i = 0, force_interval_i = 0, force_channels_i = 0, video_auto_floor_msec = 0;
switch_event_t *event; switch_event_t *event;
int scale_h264_canvas_width = 0;
int scale_h264_canvas_height = 0;
int scale_h264_canvas_fps_divisor = 0;
char *scale_h264_canvas_bandwidth = NULL;
/* Validate the conference name */ /* Validate the conference name */
if (zstr(name)) { if (zstr(name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Record! no name.\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Record! no name.\n");
@ -2720,6 +2740,28 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co
} else { } else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "video-mode invalid, valid settings are 'passthrough', 'transcode' and 'mux'\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "video-mode invalid, valid settings are 'passthrough', 'transcode' and 'mux'\n");
} }
} else if (!strcasecmp(var, "scale-h264-canvas-size") && !zstr(val)) {
char *p;
if ((scale_h264_canvas_width = atoi(val))) {
if ((p = strchr(val, 'x'))) {
p++;
if (*p) {
scale_h264_canvas_height = atoi(p);
}
}
}
if (scale_h264_canvas_width < 320 || scale_h264_canvas_width < 180) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid scale-h264-canvas-size, falling back to 320x180\n");
scale_h264_canvas_width = 320;
scale_h264_canvas_width = 180;
}
} else if (!strcasecmp(var, "scale-h264-canvas-fps-divisor") && !zstr(val)) {
scale_h264_canvas_fps_divisor = atoi(val);
if (scale_h264_canvas_fps_divisor < 0) scale_h264_canvas_fps_divisor = 0;
} else if (!strcasecmp(var, "scale-h264-canvas-bandwidth") && !zstr(val)) {
scale_h264_canvas_bandwidth = val;
} }
} }
@ -2783,6 +2825,11 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co
conference->conference_video_mode = conference_video_mode; conference->conference_video_mode = conference_video_mode;
conference->scale_h264_canvas_width = scale_h264_canvas_width;
conference->scale_h264_canvas_height = scale_h264_canvas_height;
conference->scale_h264_canvas_fps_divisor = scale_h264_canvas_fps_divisor;
conference->scale_h264_canvas_bandwidth = switch_core_strdup(conference->pool, scale_h264_canvas_bandwidth);
if (!switch_core_has_video() && (conference->conference_video_mode == CONF_VIDEO_MODE_MUX || conference->conference_video_mode == CONF_VIDEO_MODE_TRANSCODE)) { if (!switch_core_has_video() && (conference->conference_video_mode == CONF_VIDEO_MODE_MUX || conference->conference_video_mode == CONF_VIDEO_MODE_TRANSCODE)) {
conference->conference_video_mode = CONF_VIDEO_MODE_PASSTHROUGH; conference->conference_video_mode = CONF_VIDEO_MODE_PASSTHROUGH;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "video-mode invalid, only valid setting is 'passthrough' due to no video capabilities\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "video-mode invalid, only valid setting is 'passthrough' due to no video capabilities\n");
@ -2860,6 +2907,8 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co
} }
} }
conference->video_codec_settings.video.try_hardware_encoder = 1;
if (zstr(video_layout_name)) { if (zstr(video_layout_name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No video-layout-name specified, using " CONFERENCE_MUX_DEFAULT_LAYOUT "\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No video-layout-name specified, using " CONFERENCE_MUX_DEFAULT_LAYOUT "\n");
video_layout_name = CONFERENCE_MUX_DEFAULT_LAYOUT; video_layout_name = CONFERENCE_MUX_DEFAULT_LAYOUT;

View File

@ -208,6 +208,7 @@ typedef enum {
MFLAG_CAN_BE_SEEN, MFLAG_CAN_BE_SEEN,
MFLAG_SECOND_SCREEN, MFLAG_SECOND_SCREEN,
MFLAG_SILENT, MFLAG_SILENT,
MFLAG_FLIP_VIDEO,
/////////////////////////// ///////////////////////////
MFLAG_MAX MFLAG_MAX
} member_flag_t; } member_flag_t;
@ -439,6 +440,7 @@ typedef struct mcu_layer_s {
int mute_patched; int mute_patched;
int avatar_patched; int avatar_patched;
int refresh; int refresh;
int clear;
int is_avatar; int is_avatar;
switch_size_t last_img_addr; switch_size_t last_img_addr;
switch_img_position_t logo_pos; switch_img_position_t logo_pos;
@ -484,6 +486,8 @@ typedef struct mcu_canvas_s {
int refresh; int refresh;
int send_keyframe; int send_keyframe;
int play_file; int play_file;
int video_count;
char *video_layout_group;
switch_rgb_color_t bgcolor; switch_rgb_color_t bgcolor;
switch_rgb_color_t border_color; switch_rgb_color_t border_color;
switch_rgb_color_t letterbox_bgcolor; switch_rgb_color_t letterbox_bgcolor;
@ -662,6 +666,12 @@ typedef struct conference_obj {
video_layout_t *new_personal_vlayout; video_layout_t *new_personal_vlayout;
int max_bw_in; int max_bw_in;
int force_bw_in; int force_bw_in;
/* special use case, scalling shared h264 canvas*/
int scale_h264_canvas_width;
int scale_h264_canvas_height;
int scale_h264_canvas_fps_divisor;
char *scale_h264_canvas_bandwidth;
} conference_obj_t; } conference_obj_t;
/* Relationship with another member */ /* Relationship with another member */
@ -793,6 +803,9 @@ typedef struct codec_set_s {
switch_codec_t codec; switch_codec_t codec;
switch_frame_t frame; switch_frame_t frame;
uint8_t *packet; uint8_t *packet;
switch_image_t *scaled_img;
uint8_t fps_divisor;
uint32_t frame_count;
} codec_set_t; } codec_set_t;
typedef void (*conference_key_callback_t) (conference_member_t *, struct caller_control_actions *); typedef void (*conference_key_callback_t) (conference_member_t *, struct caller_control_actions *);
@ -955,6 +968,7 @@ int conference_member_noise_gate_check(conference_member_t *member);
void conference_member_check_channels(switch_frame_t *frame, conference_member_t *member, switch_bool_t in); void conference_member_check_channels(switch_frame_t *frame, conference_member_t *member, switch_bool_t in);
void conference_fnode_toggle_pause(conference_file_node_t *fnode, switch_stream_handle_t *stream); void conference_fnode_toggle_pause(conference_file_node_t *fnode, switch_stream_handle_t *stream);
void conference_fnode_check_status(conference_file_node_t *fnode, switch_stream_handle_t *stream);
// static conference_relationship_t *conference_member_get_relationship(conference_member_t *member, conference_member_t *other_member); // static conference_relationship_t *conference_member_get_relationship(conference_member_t *member, conference_member_t *other_member);
// static void conference_list(conference_obj_t *conference, switch_stream_handle_t *stream, char *delim); // static void conference_list(conference_obj_t *conference, switch_stream_handle_t *stream, char *delim);
@ -1050,6 +1064,7 @@ switch_status_t conference_api_sub_position(conference_member_t *member, switch_
switch_status_t conference_api_sub_conference_video_vmute_snap(conference_member_t *member, switch_stream_handle_t *stream, void *data); switch_status_t conference_api_sub_conference_video_vmute_snap(conference_member_t *member, switch_stream_handle_t *stream, void *data);
switch_status_t conference_api_sub_dtmf(conference_member_t *member, switch_stream_handle_t *stream, void *data); switch_status_t conference_api_sub_dtmf(conference_member_t *member, switch_stream_handle_t *stream, void *data);
switch_status_t conference_api_sub_pause_play(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv); switch_status_t conference_api_sub_pause_play(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
switch_status_t conference_api_sub_play_status(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
switch_status_t conference_api_sub_play(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv); switch_status_t conference_api_sub_play(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
switch_status_t conference_api_sub_say(conference_obj_t *conference, switch_stream_handle_t *stream, const char *text); switch_status_t conference_api_sub_say(conference_obj_t *conference, switch_stream_handle_t *stream, const char *text);
switch_status_t conference_api_sub_dial(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv); switch_status_t conference_api_sub_dial(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
@ -1091,6 +1106,7 @@ switch_status_t conference_api_sub_watching_canvas(conference_member_t *member,
switch_status_t conference_api_sub_canvas(conference_member_t *member, switch_stream_handle_t *stream, void *data); switch_status_t conference_api_sub_canvas(conference_member_t *member, switch_stream_handle_t *stream, void *data);
switch_status_t conference_api_sub_layer(conference_member_t *member, switch_stream_handle_t *stream, void *data); switch_status_t conference_api_sub_layer(conference_member_t *member, switch_stream_handle_t *stream, void *data);
switch_status_t conference_api_sub_kick(conference_member_t *member, switch_stream_handle_t *stream, void *data); switch_status_t conference_api_sub_kick(conference_member_t *member, switch_stream_handle_t *stream, void *data);
switch_status_t conference_api_sub_vid_flip(conference_member_t *member, switch_stream_handle_t *stream, void *data);
switch_status_t conference_api_sub_transfer(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv); switch_status_t conference_api_sub_transfer(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
switch_status_t conference_api_sub_record(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv); switch_status_t conference_api_sub_record(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
switch_status_t conference_api_sub_norecord(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv); switch_status_t conference_api_sub_norecord(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);

View File

@ -82,14 +82,16 @@ SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_custom_query, globals.custom_query)
static int route_callback(void *pArg, int argc, char **argv, char **columnNames) static int route_callback(void *pArg, int argc, char **argv, char **columnNames)
{ {
route_callback_t *cbt = (route_callback_t *) pArg; route_callback_t *cbt = (route_callback_t *) pArg;
if (argc >= 6) {
switch_copy_string(cbt->gateway, argv[0], 128); switch_copy_string(cbt->gateway, argv[0], 128);
switch_copy_string(cbt->group, argv[1], 128); switch_copy_string(cbt->group, argv[1], 128);
switch_copy_string(cbt->limit, argv[2], 128); switch_copy_string(cbt->limit, argv[2], 128);
switch_copy_string(cbt->techprofile, argv[3], 128); switch_copy_string(cbt->techprofile, argv[3], 128);
switch_copy_string(cbt->acctcode, argv[4], 128); switch_copy_string(cbt->acctcode, argv[4], 128);
switch_copy_string(cbt->translated, argv[5], 60); switch_copy_string(cbt->translated, argv[5], 60);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sql for route_callback only returning %d fields\n", argc);
}
return 0; return 0;
} }

View File

@ -367,7 +367,8 @@ SWITCH_STANDARD_APP(play_fsv_function)
goto end; goto end;
} }
switch_core_session_set_read_codec(session, &codec); switch_core_session_set_read_codec(session, &codec);
switch_channel_set_flag(channel, CF_VIDEO_WRITING);
while (switch_channel_ready(channel)) { while (switch_channel_ready(channel)) {
if (read(fd, &bytes, sizeof(bytes)) != sizeof(bytes)) { if (read(fd, &bytes, sizeof(bytes)) != sizeof(bytes)) {
@ -464,9 +465,8 @@ SWITCH_STANDARD_APP(play_fsv_function)
switch_core_timer_destroy(&timer); switch_core_timer_destroy(&timer);
} }
switch_core_session_set_read_codec(session, NULL); switch_core_session_set_read_codec(session, NULL);
switch_channel_clear_flag(channel, CF_VIDEO_WRITING);
if (switch_core_codec_ready(&codec)) { if (switch_core_codec_ready(&codec)) {
switch_core_codec_destroy(&codec); switch_core_codec_destroy(&codec);
@ -510,6 +510,8 @@ SWITCH_STANDARD_APP(play_yuv_function)
switch_channel_answer(channel); switch_channel_answer(channel);
switch_core_session_request_video_refresh(session);
switch_channel_audio_sync(channel); switch_channel_audio_sync(channel);
switch_core_session_raw_read(session); switch_core_session_raw_read(session);
@ -529,7 +531,7 @@ SWITCH_STANDARD_APP(play_yuv_function)
done = switch_micro_time_now() + (to * 1000); done = switch_micro_time_now() + (to * 1000);
} }
switch_channel_set_flag(channel, CF_VIDEO_DECODED_READ); // switch_channel_set_flag(channel, CF_VIDEO_DECODED_READ);
while (switch_channel_ready(channel) && !switch_channel_test_flag(channel, CF_VIDEO)) { while (switch_channel_ready(channel) && !switch_channel_test_flag(channel, CF_VIDEO)) {
if ((++loops % 100) == 0) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Waiting for video......\n"); if ((++loops % 100) == 0) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Waiting for video......\n");
@ -555,7 +557,7 @@ SWITCH_STANDARD_APP(play_yuv_function)
yuv = img->planes[SWITCH_PLANE_PACKED]; yuv = img->planes[SWITCH_PLANE_PACKED];
// switch_channel_set_flag(channel, CF_VIDEO_PASSIVE); switch_channel_set_flag(channel, CF_VIDEO_WRITING);
//SWITCH_RTP_MAX_BUF_LEN //SWITCH_RTP_MAX_BUF_LEN
vid_buffer = switch_core_session_alloc(session, SWITCH_RTP_MAX_BUF_LEN); vid_buffer = switch_core_session_alloc(session, SWITCH_RTP_MAX_BUF_LEN);
@ -653,7 +655,7 @@ SWITCH_STANDARD_APP(play_yuv_function)
switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);
switch_core_session_video_reset(session); switch_core_session_video_reset(session);
// switch_channel_clear_flag(channel, CF_VIDEO_PASSIVE); switch_channel_clear_flag(channel, CF_VIDEO_WRITING);
} }

View File

@ -51,6 +51,18 @@ static struct {
char *memcached_str; char *memcached_str;
} globals; } globals;
#define BYTES_PER_SAMPLE 2
struct memcache_context {
memcached_st *memcached;
char *path;
int ok;
size_t offset;
size_t remaining;
void *data;
};
typedef struct memcache_context memcache_context_t;
static switch_event_node_t *NODE = NULL; static switch_event_node_t *NODE = NULL;
static switch_status_t config_callback_memcached(switch_xml_config_item_t *data, const char *newvalue, switch_config_callback_type_t callback_type, static switch_status_t config_callback_memcached(switch_xml_config_item_t *data, const char *newvalue, switch_config_callback_type_t callback_type,
@ -431,10 +443,149 @@ SWITCH_STANDARD_API(memcache_function)
return status; return status;
} }
static switch_status_t memcache_file_open(switch_file_handle_t *handle, const char *path)
{
memcache_context_t *context;
size_t string_length = 0;
uint32_t flags = 0;
memcached_return rc;
if (handle->offset_pos) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Offset unsupported.\n");
return SWITCH_STATUS_GENERR;
}
context = switch_core_alloc(handle->memory_pool, sizeof(*context));
/* Clone memcached struct so we're thread safe */
context->memcached = memcached_clone(NULL, globals.memcached);
if (!context->memcached) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error cloning memcached object\n");
return SWITCH_STATUS_FALSE;
}
/* All of the actual data is read into the buffer here, the memcache_file_read
* function just iterates over the buffer
*/
if (switch_test_flag(handle, SWITCH_FILE_FLAG_READ)) {
handle->private_info = context;
context->data = memcached_get(context->memcached, path, strlen(path), &string_length, &flags, &rc);
if (context->data && rc == MEMCACHED_SUCCESS) {
context->ok = 1;
context->offset = 0;
context->remaining = string_length / BYTES_PER_SAMPLE;
return SWITCH_STATUS_SUCCESS;
} else {
memcached_free(context->memcached);
context->memcached = NULL;
switch_safe_free(context->data);
context->data = NULL;
context->ok = 0;
return SWITCH_STATUS_FALSE;
}
} else if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
if (switch_test_flag(handle, SWITCH_FILE_WRITE_OVER)) {
memcached_free(context->memcached);
context->memcached = NULL;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unsupported file mode.\n");
return SWITCH_STATUS_GENERR;
}
context->path = switch_core_strdup(handle->memory_pool, path);
if (! switch_test_flag(handle, SWITCH_FILE_WRITE_APPEND)) {
/* If not appending, we need to write an empty string to the key so that
* memcache_file_write appends do the right thing
*/
rc = memcached_set(context->memcached, context->path, strlen(context->path), "", 0, 0, 0);
if (rc != MEMCACHED_SUCCESS) {
memcached_free(context->memcached);
context->memcached = NULL;
return SWITCH_STATUS_GENERR;
}
}
context->ok = 1;
handle->private_info = context;
return SWITCH_STATUS_SUCCESS;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File opened with unknown flags!\n");
return SWITCH_STATUS_GENERR;
}
static switch_status_t memcache_file_close(switch_file_handle_t *handle)
{
memcache_context_t *memcache_context = handle->private_info;
if (memcache_context->data) {
switch_safe_free(memcache_context->data);
memcache_context->data = NULL;
}
if (memcache_context->memcached) {
memcached_free(memcache_context->memcached);
memcache_context->memcached = NULL;
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t memcache_file_read(switch_file_handle_t *handle, void *data, size_t *len)
{
memcache_context_t *context= handle->private_info;
if (context->ok) {
if (context->remaining <= 0) {
return SWITCH_STATUS_FALSE;
}
if (*len > (size_t) context->remaining) {
*len = context->remaining;
}
memcpy(data, (uint8_t *)context->data + (context->offset * BYTES_PER_SAMPLE), *len * BYTES_PER_SAMPLE);
context->offset += (int32_t)*len;
context->remaining -= (int32_t)*len;
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
static switch_status_t memcache_file_write(switch_file_handle_t *handle, void *data, size_t *len)
{
memcache_context_t *context = handle->private_info;
memcached_return rc;
/* Append this chunk */
if (context->ok) {
rc = memcached_append(context->memcached, context->path, strlen(context->path), data, *len, 0, 0);
if (rc != MEMCACHED_SUCCESS) {
context->ok = 0;
return SWITCH_STATUS_FALSE;
}
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
static char *supported_formats[SWITCH_MAX_CODECS] = { 0 };
/* Macro expands to: switch_status_t mod_memcache_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */ /* Macro expands to: switch_status_t mod_memcache_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
SWITCH_MODULE_LOAD_FUNCTION(mod_memcache_load) SWITCH_MODULE_LOAD_FUNCTION(mod_memcache_load)
{ {
switch_api_interface_t *api_interface; switch_api_interface_t *api_interface;
switch_file_interface_t *file_interface;
/* connect my internal structure to the blank pointer passed to me */ /* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname); *module_interface = switch_loadable_module_create_module_interface(pool, modname);
@ -449,6 +600,16 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_memcache_load)
SWITCH_ADD_API(api_interface, "memcache", "Memcache API", memcache_function, "syntax"); SWITCH_ADD_API(api_interface, "memcache", "Memcache API", memcache_function, "syntax");
/* file interface */
supported_formats[0] = "memcache";
file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
file_interface->interface_name = modname;
file_interface->extens = supported_formats;
file_interface->file_open = memcache_file_open;
file_interface->file_close = memcache_file_close;
file_interface->file_read = memcache_file_read;
file_interface->file_write = memcache_file_write;
/* indicate that the module should continue to be loaded */ /* indicate that the module should continue to be loaded */
return SWITCH_STATUS_NOUNLOAD; return SWITCH_STATUS_NOUNLOAD;
} }

View File

@ -333,18 +333,16 @@ static switch_status_t switch_sangoma_init(switch_codec_t *codec, switch_codec_f
if (!(sess = switch_core_alloc(codec->memory_pool, sizeof(*sess)))) { if (!(sess = switch_core_alloc(codec->memory_pool, sizeof(*sess)))) {
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
} }
memset(sess, 0, sizeof(*sess));
sess->encoder.lastrxseqno = -1; sess->encoder.lastrxseqno = -1;
sess->decoder.lastrxseqno = -1; sess->decoder.lastrxseqno = -1;
sess->pool = codec->memory_pool; sess->pool = codec->memory_pool;
sess->impl = codec->implementation; sess->impl = codec->implementation;
switch_assert(sess->pool);
switch_assert(sess->impl);
vcodec = get_codec_from_iana(codec->implementation->ianacode, codec->implementation->bits_per_second); vcodec = get_codec_from_iana(codec->implementation->ianacode, codec->implementation->bits_per_second);
switch_mutex_lock(g_sessions_lock);
if (encoding) { if (encoding) {
sess->encoder.request.usr_priv = sess; sess->encoder.request.usr_priv = sess;
sess->encoder.request.a.host_ip = g_rtpip; sess->encoder.request.a.host_ip = g_rtpip;
@ -370,13 +368,15 @@ static switch_status_t switch_sangoma_init(switch_codec_t *codec, switch_codec_f
} }
switch_mutex_lock(g_sessions_lock);
sess->sessid = g_next_session_id++; sess->sessid = g_next_session_id++;
switch_snprintf(sess->hashkey, sizeof(sess->hashkey), SANGOMA_SESS_HASH_KEY_FORMAT, sess->sessid); switch_snprintf(sess->hashkey, sizeof(sess->hashkey), SANGOMA_SESS_HASH_KEY_FORMAT, sess->sessid);
switch_core_hash_insert(g_sessions_hash, sess->hashkey, sess); switch_core_hash_insert(g_sessions_hash, sess->hashkey, sess);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sangoma init done for codec %s/%s, iana = %d\n", codec->implementation->iananame, vcodec->fs_name, codec->implementation->ianacode);
switch_mutex_unlock(g_sessions_lock); switch_mutex_unlock(g_sessions_lock);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sangoma init done for codec %s/%s, iana = %d\n", codec->implementation->iananame, vcodec->fs_name, codec->implementation->ianacode);
codec->private_info = sess; codec->private_info = sess;
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
@ -864,14 +864,13 @@ static switch_status_t switch_sangoma_destroy(switch_codec_t *codec)
if (sess->encoder.txrtp) { if (sess->encoder.txrtp) {
sngtc_free_transcoding_session(&sess->encoder.reply); sngtc_free_transcoding_session(&sess->encoder.reply);
memset(&sess->encoder, 0, sizeof(sess->encoder));
} }
if (sess->decoder.txrtp) { if (sess->decoder.txrtp) {
sngtc_free_transcoding_session(&sess->decoder.reply); sngtc_free_transcoding_session(&sess->decoder.reply);
memset(&sess->decoder, 0, sizeof(sess->decoder));
} }
switch_core_hash_delete(g_sessions_hash, sess->hashkey); switch_core_hash_delete(g_sessions_hash, sess->hashkey);
memset(sess, 0, sizeof(*sess));
switch_mutex_unlock(g_sessions_lock); switch_mutex_unlock(g_sessions_lock);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;

View File

@ -911,7 +911,6 @@ switch_status_t rtmp_session_request(rtmp_profile_t *profile, rtmp_session_t **n
{ {
char buf[1024]; char buf[1024];
#ifndef _WIN32 #ifndef _WIN32
#else
snprintf(buf, sizeof(buf), "/tmp/rtmp-%s-in.txt", (*newsession)->uuid); snprintf(buf, sizeof(buf), "/tmp/rtmp-%s-in.txt", (*newsession)->uuid);
(*newsession)->io_debug_in = fopen(buf, "w"); (*newsession)->io_debug_in = fopen(buf, "w");
snprintf(buf, sizeof(buf), "/tmp/rtmp-%s-out.txt", (*newsession)->uuid); snprintf(buf, sizeof(buf), "/tmp/rtmp-%s-out.txt", (*newsession)->uuid);

View File

@ -515,6 +515,7 @@ struct rtmp_session {
uint32_t media_streamid; /* < The stream id that was used for the last "play" command, uint32_t media_streamid; /* < The stream id that was used for the last "play" command,
where we should send media */ where we should send media */
switch_size_t dropped_video_frame; switch_size_t dropped_video_frame;
switch_queue_t *video_send_queue;
uint8_t media_debug; uint8_t media_debug;
}; };

View File

@ -41,6 +41,17 @@ typedef struct {
size_t len; size_t len;
} buffer_helper_t; } buffer_helper_t;
typedef struct {
uint8_t amfnumber;
uint32_t timestamp;
uint8_t type;
uint32_t stream_id;
switch_size_t len;
uint32_t flags;
unsigned char *message;
} video_send_buffer_t;
size_t my_buffer_read(void * out_buffer, size_t size, void * user_data) size_t my_buffer_read(void * out_buffer, size_t size, void * user_data)
{ {
buffer_helper_t *helper = (buffer_helper_t*)user_data; buffer_helper_t *helper = (buffer_helper_t*)user_data;
@ -561,8 +572,62 @@ switch_status_t rtmp_send_invoke_v(rtmp_session_t *rsession, uint8_t amfnumber,
return rtmp_send_message(rsession, amfnumber, timestamp, type, stream_id, buf, helper.pos, 0); return rtmp_send_message(rsession, amfnumber, timestamp, type, stream_id, buf, helper.pos, 0);
} }
static int flush_video_send_queue(rtmp_session_t *rsession, switch_bool_t lock)
{
video_send_buffer_t *b;
void *pop;
switch_queue_t *q = rsession->video_send_queue;
int x = 0;
if (!q) return 0;
if (lock) switch_mutex_lock(rsession->socket_mutex);
while (switch_queue_size(q) > 0 && switch_queue_trypop(q, &pop) == SWITCH_STATUS_SUCCESS && pop) {
b = (video_send_buffer_t *)pop;
free(b->message);
free(b);
x++;
}
if (lock) switch_mutex_unlock(rsession->socket_mutex);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Dropped %d Video Frames\n", x);
return x;
}
static void buffer_video_send(rtmp_session_t *rsession, uint8_t amfnumber, uint32_t timestamp, uint8_t type, uint32_t stream_id, const unsigned char *message, switch_size_t len, uint32_t flags)
{
video_send_buffer_t *vbuf;
switch_mutex_lock(rsession->socket_mutex);
if (!rsession->video_send_queue) {
switch_queue_create(&rsession->video_send_queue, 1000, rsession->pool);
}
if (*message == 0x17) {
flush_video_send_queue(rsession, SWITCH_FALSE);
}
vbuf = malloc(sizeof(video_send_buffer_t));
switch_assert(vbuf);
vbuf->amfnumber = amfnumber;
vbuf->timestamp = timestamp;
vbuf->type = type;
vbuf->stream_id = stream_id;
vbuf->len = len;
vbuf->flags = flags;
vbuf->message = malloc(len);
switch_assert(vbuf->message);
memcpy(vbuf->message, message, len);
switch_queue_push(rsession->video_send_queue, (void *)vbuf);
switch_mutex_unlock(rsession->socket_mutex);
}
/* Break message down into 128 bytes chunks, add the appropriate headers and send it out */ /* Break message down into 128 bytes chunks, add the appropriate headers and send it out */
switch_status_t rtmp_send_message(rtmp_session_t *rsession, uint8_t amfnumber, uint32_t timestamp, uint8_t type, uint32_t stream_id, const unsigned char *message, switch_size_t len, uint32_t flags) switch_status_t _rtmp_send_message(rtmp_session_t *rsession, uint8_t amfnumber, uint32_t timestamp, uint8_t type, uint32_t stream_id, const unsigned char *message, switch_size_t len, uint32_t flags)
{ {
switch_size_t pos = 0; switch_size_t pos = 0;
uint8_t header[12] = { amfnumber & 0x3F, INT24(0), INT24(len), type, INT32_LE(stream_id) }; uint8_t header[12] = { amfnumber & 0x3F, INT24(0), INT24(len), type, INT32_LE(stream_id) };
@ -575,52 +640,6 @@ switch_status_t rtmp_send_message(rtmp_session_t *rsession, uint8_t amfnumber, u
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%d send_ack=%d send=%d window=%d wait_ack=%d\n", // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%d send_ack=%d send=%d window=%d wait_ack=%d\n",
// type, rsession->send_ack, rsession->send, rsession->send_ack_window, rsession->send + 3073 - rsession->send_ack); // type, rsession->send_ack, rsession->send, rsession->send_ack_window, rsession->send + 3073 - rsession->send_ack);
if (type == RTMP_TYPE_VIDEO) {
uint32_t window = rsession->send_ack_window;
if (rsession->media_debug & RTMP_MD_VIDEO_WRITE) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "W V ts:%u data:0x%02x len:%" SWITCH_SIZE_T_FMT "\n", timestamp, *message, len);
}
/* start to drop video frame on window/2 if the frame is a non-IDR video frame
start to drop video frame on window * 3/4 if the frame is a IDR frame
start to drop audio frame on widnow full
*/
if (*message == 0x17) {
window = window / 4 * 3;
} else {
window /= 2;
}
if ((rsession->send_ack + window) < (rsession->send + 3073)) {
/* We're sending too fast, drop the frame */
rsession->dropped_video_frame++;
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_DEBUG,
"DROP VIDEO FRAME [amfnumber=%d type=0x%x stream_id=0x%x ftype=0x%x] len=%"SWITCH_SIZE_T_FMT
" dropped=%"SWITCH_SIZE_T_FMT"\n",
amfnumber, type, stream_id, *message, len, rsession->dropped_video_frame);
return SWITCH_STATUS_SUCCESS;
}
if (rsession->dropped_video_frame) {
if (*message != 0x17) {
rsession->dropped_video_frame++;
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_DEBUG,
"DROP VIDEO FRAME [amfnumber=%d type=0x%x stream_id=0x%x ftype=0x%x] len=%"SWITCH_SIZE_T_FMT
" dropped=%"SWITCH_SIZE_T_FMT" waiting for the next IDR\n",
amfnumber, type, stream_id, *message, len, rsession->dropped_video_frame);
return SWITCH_STATUS_SUCCESS;
} else {
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_INFO,
"Got IDR frame after %"SWITCH_SIZE_T_FMT" frame(s) dropped\n",
rsession->dropped_video_frame);
rsession->dropped_video_frame = 0;
}
}
}
if (type == RTMP_TYPE_AUDIO && (rsession->media_debug & RTMP_MD_AUDIO_WRITE)) { if (type == RTMP_TYPE_AUDIO && (rsession->media_debug & RTMP_MD_AUDIO_WRITE)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "W A ts:%u data:0x%02x len:%" SWITCH_SIZE_T_FMT "\n", timestamp, *message, len); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "W A ts:%u data:0x%02x len:%" SWITCH_SIZE_T_FMT "\n", timestamp, *message, len);
} }
@ -696,6 +715,8 @@ switch_status_t rtmp_send_message(rtmp_session_t *rsession, uint8_t amfnumber, u
header[3] = timestamp & 0xFF; header[3] = timestamp & 0xFF;
} }
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "=== send type: %d ts: %d bytes: %zu\n", type, timestamp, len);
state->ts = timestamp; state->ts = timestamp;
state->type = type; state->type = type;
state->origlen = len; state->origlen = len;
@ -740,6 +761,79 @@ end:
return status; return status;
} }
switch_status_t rtmp_send_message(rtmp_session_t *rsession, uint8_t amfnumber, uint32_t timestamp, uint8_t type, uint32_t stream_id, const unsigned char *message, switch_size_t len, uint32_t flags)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
int window = rsession->send_ack_window;
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%d send_ack=%d send=%d window=%d wait_ack=%d\n",
// type, rsession->send_ack, rsession->send, rsession->send_ack_window, rsession->send + 3073 - rsession->send_ack);
if (type != RTMP_TYPE_VIDEO) {
return _rtmp_send_message(rsession, amfnumber, timestamp, type, stream_id, message, len, flags);
}
if (rsession->media_debug & RTMP_MD_VIDEO_WRITE) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "W V ts:%u data:0x%02x len:%" SWITCH_SIZE_T_FMT "\n", timestamp, *message, len);
}
window = window / 4 * 3;
// window = 65000;
if ((rsession->send_ack + window) < (rsession->send + 3073)) {
buffer_video_send(rsession, amfnumber, timestamp, type, stream_id, message, len, flags);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "queued %zu bytes, ts: %d, queue size:%d\n", len, timestamp, switch_queue_size(rsession->video_send_queue));
return SWITCH_STATUS_SUCCESS;
}
if (rsession->video_send_queue && switch_queue_size(rsession->video_send_queue)) {
if (*message == 0x17) { // key frame
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Got a key frame, flush video queue %d\n", switch_queue_size(rsession->video_send_queue));
flush_video_send_queue(rsession, SWITCH_TRUE);
return _rtmp_send_message(rsession, amfnumber, timestamp, type, stream_id, message, len, flags);
} else {
int x = 0;
void *pop = NULL;
buffer_video_send(rsession, amfnumber, timestamp, type, stream_id, message, len, flags);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "queued %zu bytes, ts: %d, queue size:%d\n", len, timestamp, switch_queue_size(rsession->video_send_queue));
again:
switch_mutex_lock(rsession->socket_mutex);
switch_queue_trypop(rsession->video_send_queue, &pop);
switch_mutex_unlock(rsession->socket_mutex);
if (pop) {
video_send_buffer_t *vbuf = (video_send_buffer_t *)pop;
amfnumber = vbuf->amfnumber;
// timestamp = vbuf->timestamp;
type = vbuf->type;
stream_id = vbuf->stream_id;
len = vbuf->len;
flags = vbuf->flags;
message = vbuf->message;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pop len: %zu, ts: %d, queue size: %d\n", len, timestamp, switch_queue_size(rsession->video_send_queue));
status = _rtmp_send_message(rsession, amfnumber, timestamp, type, stream_id, message, len, flags);
free(vbuf->message);
free(vbuf);
if (status == SWITCH_STATUS_SUCCESS && ((rsession->send_ack + window) >= (rsession->send + 3073) && (++x < 3))) {
pop = NULL;
goto again;
}
}
}
} else {
return _rtmp_send_message(rsession, amfnumber, timestamp, type, stream_id, message, len, flags);
}
return status;
}
/* Returns SWITCH_STATUS_SUCCESS of the connection is still active or SWITCH_STATUS_FALSE to tear it down */ /* Returns SWITCH_STATUS_SUCCESS of the connection is still active or SWITCH_STATUS_FALSE to tear it down */
switch_status_t rtmp_handle_data(rtmp_session_t *rsession) switch_status_t rtmp_handle_data(rtmp_session_t *rsession)
{ {

View File

@ -301,6 +301,10 @@ switch_status_t rtmp_tcp_init(rtmp_profile_t *profile, const char *bindaddr, rtm
if (switch_socket_opt_set(io_tcp->listen_socket, SWITCH_SO_TCP_NODELAY, 1)) { if (switch_socket_opt_set(io_tcp->listen_socket, SWITCH_SO_TCP_NODELAY, 1)) {
goto fail; goto fail;
} }
if (1) {
switch_socket_opt_set(io_tcp->listen_socket, SWITCH_SO_RCVBUF, 1572864);
switch_socket_opt_set(io_tcp->listen_socket, SWITCH_SO_SNDBUF, 1572864);
}
if (switch_socket_bind(io_tcp->listen_socket, sa)) { if (switch_socket_bind(io_tcp->listen_socket, sa)) {
goto fail; goto fail;
} }

View File

@ -583,7 +583,7 @@ switch_status_t rtmp_write_video_frame(switch_core_session_t *session, switch_fr
rtmp_rtp2rtmpH264(helper, frame); rtmp_rtp2rtmpH264(helper, frame);
if (helper->send) { if (helper->send) {
uint16_t used = switch_buffer_inuse(helper->rtmp_buf); uint32_t used = switch_buffer_inuse(helper->rtmp_buf);
const void *rtmp_data = NULL; const void *rtmp_data = NULL;
switch_buffer_peek_zerocopy(helper->rtmp_buf, &rtmp_data); switch_buffer_peek_zerocopy(helper->rtmp_buf, &rtmp_data);
@ -633,6 +633,11 @@ switch_status_t rtmp_write_video_frame(switch_core_session_t *session, switch_fr
switch_core_session_rwunlock(other_session); switch_core_session_rwunlock(other_session);
} }
} }
if (rsession->video_send_queue && switch_queue_size(rsession->video_send_queue) > 30) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Need a key frame\n");
switch_channel_set_flag(channel, CF_VIDEO_REFRESH_REQ);
}
skip: skip:
switch_buffer_zero(helper->rtmp_buf); switch_buffer_zero(helper->rtmp_buf);
switch_buffer_zero(helper->fua_buf); switch_buffer_zero(helper->fua_buf);

View File

@ -320,7 +320,7 @@ void skinny_line_get(listener_t *listener, uint32_t instance, struct line_stat_r
switch_assert(listener->profile); switch_assert(listener->profile);
switch_assert(listener->device_name); switch_assert(listener->device_name);
helper.button = switch_core_alloc(listener->pool, sizeof(struct line_stat_res_message)); helper.button = calloc(sizeof(struct line_stat_res_message),1);
if ((sql = switch_mprintf( if ((sql = switch_mprintf(
"SELECT '%d' AS wanted_position, position, label, value, caller_name " "SELECT '%d' AS wanted_position, position, label, value, caller_name "
@ -363,7 +363,7 @@ void skinny_speed_dial_get(listener_t *listener, uint32_t instance, struct speed
switch_assert(listener->profile); switch_assert(listener->profile);
switch_assert(listener->device_name); switch_assert(listener->device_name);
helper.button = switch_core_alloc(listener->pool, sizeof(struct speed_dial_stat_res_message)); helper.button = calloc(sizeof(struct speed_dial_stat_res_message),1);
if ((sql = switch_mprintf( if ((sql = switch_mprintf(
"SELECT '%d' AS wanted_position, position, label, value, settings " "SELECT '%d' AS wanted_position, position, label, value, settings "
@ -407,7 +407,7 @@ void skinny_service_url_get(listener_t *listener, uint32_t instance, struct serv
switch_assert(listener->profile); switch_assert(listener->profile);
switch_assert(listener->device_name); switch_assert(listener->device_name);
helper.button = switch_core_alloc(listener->pool, sizeof(struct service_url_stat_res_message)); helper.button = calloc(sizeof(struct service_url_stat_res_message), 1);
if ((sql = switch_mprintf( if ((sql = switch_mprintf(
"SELECT '%d' AS wanted_position, position, label, value, settings " "SELECT '%d' AS wanted_position, position, label, value, settings "
@ -453,7 +453,7 @@ void skinny_feature_get(listener_t *listener, uint32_t instance, struct feature_
switch_assert(listener->profile); switch_assert(listener->profile);
switch_assert(listener->device_name); switch_assert(listener->device_name);
helper.button = switch_core_alloc(listener->pool, sizeof(struct feature_stat_res_message)); helper.button = calloc(sizeof(struct feature_stat_res_message), 1);
if ((sql = switch_mprintf( if ((sql = switch_mprintf(
"SELECT '%d' AS wanted_position, position, label, value, settings " "SELECT '%d' AS wanted_position, position, label, value, settings "

View File

@ -116,6 +116,9 @@ switch_status_t skinny_create_incoming_session(listener_t *listener, uint32_t *l
if (!button || !button->shortname[0]) { if (!button || !button->shortname[0]) {
skinny_log_l(listener, SWITCH_LOG_CRIT, "Line %d not found on device\n", *line_instance_p); skinny_log_l(listener, SWITCH_LOG_CRIT, "Line %d not found on device\n", *line_instance_p);
if ( button ) {
switch_safe_free(button);
}
goto error; goto error;
} }
@ -199,11 +202,17 @@ error:
} }
listener->profile->ib_failed_calls++; listener->profile->ib_failed_calls++;
if ( button ) {
switch_safe_free(button);
}
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
done: done:
*session = nsession; *session = nsession;
listener->profile->ib_calls++; listener->profile->ib_calls++;
if ( button ) {
switch_safe_free(button);
}
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
@ -1524,6 +1533,7 @@ switch_status_t skinny_handle_stimulus_message(listener_t *listener, skinny_mess
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
} }
skinny_session_process_dest(session, listener, line_instance, button_speed_dial->line, '\0', 0); skinny_session_process_dest(session, listener, line_instance, button_speed_dial->line, '\0', 0);
switch_safe_free(button_speed_dial);
} }
break; break;
case SKINNY_BUTTON_HOLD: case SKINNY_BUTTON_HOLD:
@ -1582,10 +1592,12 @@ switch_status_t skinny_handle_stimulus_message(listener_t *listener, skinny_mess
skinny_create_incoming_session(listener, &line_instance, &session); skinny_create_incoming_session(listener, &line_instance, &session);
if ( ! session ) { if ( ! session ) {
skinny_log_l_msg(listener, SWITCH_LOG_CRIT, "Unable to handle stimulus message, couldn't create incoming session.\n"); skinny_log_l_msg(listener, SWITCH_LOG_CRIT, "Unable to handle stimulus message, couldn't create incoming session.\n");
switch_safe_free(button_line);
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
} }
skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 0); skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 0);
} }
switch_safe_free(button_line);
break; break;
default: default:
@ -1709,6 +1721,8 @@ switch_status_t skinny_handle_speed_dial_stat_request(listener_t *listener, skin
send_speed_dial_stat_res(listener, request->data.speed_dial_req.number, button->line, button->label); send_speed_dial_stat_res(listener, request->data.speed_dial_req.number, button->line, button->label);
switch_safe_free(button);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
@ -1725,6 +1739,8 @@ switch_status_t skinny_handle_line_stat_request(listener_t *listener, skinny_mes
memcpy(&message->data.line_res, button, sizeof(struct line_stat_res_message)); memcpy(&message->data.line_res, button, sizeof(struct line_stat_res_message));
switch_safe_free(button);
skinny_send_reply(listener, message, SWITCH_TRUE); skinny_send_reply(listener, message, SWITCH_TRUE);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
@ -2407,6 +2423,8 @@ switch_status_t skinny_handle_service_url_stat_request(listener_t *listener, ski
skinny_send_reply(listener, message, SWITCH_TRUE); skinny_send_reply(listener, message, SWITCH_TRUE);
switch_safe_free(button);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
@ -2425,6 +2443,8 @@ switch_status_t skinny_handle_feature_stat_request(listener_t *listener, skinny_
skinny_send_reply(listener, message, SWITCH_TRUE); skinny_send_reply(listener, message, SWITCH_TRUE);
switch_safe_free(button);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
@ -2568,7 +2588,7 @@ switch_status_t skinny_handle_updatecapabilities(listener_t *listener, skinny_me
} }
i = 0; i = 0;
pos = 0; pos = 0;
codec_string = switch_core_alloc(listener->pool, string_len+1); codec_string = calloc(string_len+1, 1);
for (string_pos = 0; string_pos < string_len; string_pos++) { for (string_pos = 0; string_pos < string_len; string_pos++) {
char *codec = codec_order[i]; char *codec = codec_order[i];
switch_assert(i < n); switch_assert(i < n);
@ -2591,6 +2611,7 @@ switch_status_t skinny_handle_updatecapabilities(listener_t *listener, skinny_me
} }
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Codecs %s supported.\n", codec_string); "Codecs %s supported.\n", codec_string);
switch_safe_free(codec_string);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }

View File

@ -343,6 +343,11 @@ switch_status_t sofia_on_destroy(switch_core_session_t *session)
if (tech_pvt) { if (tech_pvt) {
if (tech_pvt->proxy_refer_msg) {
msg_ref_destroy(tech_pvt->proxy_refer_msg);
tech_pvt->proxy_refer_msg = NULL;
}
if (tech_pvt->respond_phrase) { if (tech_pvt->respond_phrase) {
switch_yield(100000); switch_yield(100000);
} }
@ -598,6 +603,15 @@ switch_status_t sofia_on_hangup(switch_core_session_t *session)
switch_safe_free(bye_headers); switch_safe_free(bye_headers);
} }
if (cause == SWITCH_CAUSE_WRONG_CALL_STATE) {
switch_event_t *s_event;
if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_WRONG_CALL_STATE) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "network_ip", tech_pvt->mparams.remote_ip);
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "network_port", "%d", tech_pvt->mparams.remote_port);
switch_event_fire(&s_event);
}
}
sofia_clear_flag(tech_pvt, TFLAG_IO); sofia_clear_flag(tech_pvt, TFLAG_IO);
if (tech_pvt->sofia_private) { if (tech_pvt->sofia_private) {
@ -1318,9 +1332,46 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
switch (msg->message_id) { switch (msg->message_id) {
#if 1 case SWITCH_MESSAGE_INDICATE_DEFLECT: {
char *extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_HEADER_PREFIX);
char ref_to[1024] = "";
const char *var;
if (!strcasecmp(msg->string_arg, "sip:")) {
const char *format = strchr(tech_pvt->profile->sipip, ':') ? "sip:%s@[%s]" : "sip:%s@%s";
switch_snprintf(ref_to, sizeof(ref_to), format, msg->string_arg, tech_pvt->profile->sipip);
} else {
switch_set_string(ref_to, msg->string_arg);
}
nua_refer(tech_pvt->nh, SIPTAG_REFER_TO_STR(ref_to), SIPTAG_REFERRED_BY_STR(tech_pvt->contact_url),
TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
TAG_END());
if (msg->string_array_arg[0]) {
tech_pvt->proxy_refer_uuid = (char *)msg->string_array_arg[0];
} else {
switch_mutex_unlock(tech_pvt->sofia_mutex);
sofia_wait_for_reply(tech_pvt, 9999, 10);
switch_mutex_lock(tech_pvt->sofia_mutex);
if ((var = switch_channel_get_variable(tech_pvt->channel, "sip_refer_reply"))) {
msg->string_reply = switch_core_session_strdup(session, var);
} else {
msg->string_reply = "no reply";
}
switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_BLIND_TRANSFER);
}
switch_safe_free(extra_headers);
}
break;
case SWITCH_MESSAGE_INDICATE_VIDEO_REFRESH_REQ: case SWITCH_MESSAGE_INDICATE_VIDEO_REFRESH_REQ:
{ if (!switch_channel_test_flag(channel, CF_AVPF)) {
//const char *ua = switch_channel_get_variable(tech_pvt->channel, "sip_user_agent"); //const char *ua = switch_channel_get_variable(tech_pvt->channel, "sip_user_agent");
//if (ua && switch_stristr("polycom", ua)) { //if (ua && switch_stristr("polycom", ua)) {
@ -1341,7 +1392,6 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
//} //}
} }
break; break;
#endif
case SWITCH_MESSAGE_INDICATE_BROADCAST: case SWITCH_MESSAGE_INDICATE_BROADCAST:
{ {
const char *ip = NULL, *port = NULL; const char *ip = NULL, *port = NULL;
@ -1945,34 +1995,6 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
} }
} }
break; break;
case SWITCH_MESSAGE_INDICATE_DEFLECT:
{
char *extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_HEADER_PREFIX);
char ref_to[1024] = "";
const char *var;
if (!strcasecmp(msg->string_arg, "sip:")) {
const char *format = strchr(tech_pvt->profile->sipip, ':') ? "sip:%s@[%s]" : "sip:%s@%s";
switch_snprintf(ref_to, sizeof(ref_to), format, msg->string_arg, tech_pvt->profile->sipip);
} else {
switch_set_string(ref_to, msg->string_arg);
}
nua_refer(tech_pvt->nh, SIPTAG_REFER_TO_STR(ref_to), SIPTAG_REFERRED_BY_STR(tech_pvt->contact_url),
TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
TAG_END());
switch_mutex_unlock(tech_pvt->sofia_mutex);
sofia_wait_for_reply(tech_pvt, 9999, 10);
switch_mutex_lock(tech_pvt->sofia_mutex);
if ((var = switch_channel_get_variable(tech_pvt->channel, "sip_refer_reply"))) {
msg->string_reply = switch_core_session_strdup(session, var);
} else {
msg->string_reply = "no reply";
}
switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_BLIND_TRANSFER);
switch_safe_free(extra_headers);
}
break;
case SWITCH_MESSAGE_INDICATE_RESPOND: case SWITCH_MESSAGE_INDICATE_RESPOND:
{ {
@ -2009,6 +2031,16 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
} }
} }
if (tech_pvt->proxy_refer_uuid) {
if (tech_pvt->proxy_refer_msg) {
nua_respond(tech_pvt->nh, code, su_strdup(nua_handle_home(tech_pvt->nh), reason), SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_EXPIRES_STR("60"), NUTAG_WITH_THIS_MSG(tech_pvt->proxy_refer_msg), TAG_END());
msg_ref_destroy(tech_pvt->proxy_refer_msg);
tech_pvt->proxy_refer_msg = NULL;
}
goto end_lock;
}
if (code == 302 && !zstr(msg->string_arg)) { if (code == 302 && !zstr(msg->string_arg)) {
char *p; char *p;
@ -5008,7 +5040,7 @@ static int notify_callback(void *pArg, int argc, char **argv, char **columnNames
return 0; return 0;
} }
static void general_event_handler(switch_event_t *event) void general_event_handler(switch_event_t *event)
{ {
switch (event->event_id) { switch (event->event_id) {
case SWITCH_EVENT_NOTIFY: case SWITCH_EVENT_NOTIFY:
@ -5546,6 +5578,14 @@ static void general_event_handler(switch_event_t *event)
} }
} }
static void general_queue_event_handler(switch_event_t *event)
{
switch_event_t *dup;
switch_event_dup(&dup, event);
switch_queue_push(mod_sofia_globals.general_event_queue, dup);
}
void write_csta_xml_chunk(switch_event_t *event, switch_stream_handle_t stream, const char *csta_event, char *fwdtype) void write_csta_xml_chunk(switch_event_t *event, switch_stream_handle_t stream, const char *csta_event, char *fwdtype)
{ {
const char *device = switch_event_get_header(event, "device"); const char *device = switch_event_get_header(event, "device");
@ -5882,6 +5922,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_sofia_load)
mod_sofia_globals.auto_nat = (switch_nat_get_type() ? 1 : 0); mod_sofia_globals.auto_nat = (switch_nat_get_type() ? 1 : 0);
switch_queue_create(&mod_sofia_globals.presence_queue, SOFIA_QUEUE_SIZE, mod_sofia_globals.pool); switch_queue_create(&mod_sofia_globals.presence_queue, SOFIA_QUEUE_SIZE, mod_sofia_globals.pool);
switch_queue_create(&mod_sofia_globals.general_event_queue, SOFIA_QUEUE_SIZE, mod_sofia_globals.pool);
mod_sofia_globals.cpu_count = switch_core_cpu_count(); mod_sofia_globals.cpu_count = switch_core_cpu_count();
mod_sofia_globals.max_msg_queues = (mod_sofia_globals.cpu_count / 2) + 1; mod_sofia_globals.max_msg_queues = (mod_sofia_globals.cpu_count / 2) + 1;
@ -5952,27 +5993,27 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_sofia_load)
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
} }
if (switch_event_bind(modname, SWITCH_EVENT_TRAP, SWITCH_EVENT_SUBCLASS_ANY, general_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { if (switch_event_bind(modname, SWITCH_EVENT_TRAP, SWITCH_EVENT_SUBCLASS_ANY, general_queue_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
} }
if (switch_event_bind(modname, SWITCH_EVENT_NOTIFY, SWITCH_EVENT_SUBCLASS_ANY, general_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { if (switch_event_bind(modname, SWITCH_EVENT_NOTIFY, SWITCH_EVENT_SUBCLASS_ANY, general_queue_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
} }
if (switch_event_bind(modname, SWITCH_EVENT_PHONE_FEATURE, SWITCH_EVENT_SUBCLASS_ANY, general_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { if (switch_event_bind(modname, SWITCH_EVENT_PHONE_FEATURE, SWITCH_EVENT_SUBCLASS_ANY, general_queue_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
} }
if (switch_event_bind(modname, SWITCH_EVENT_SEND_MESSAGE, SWITCH_EVENT_SUBCLASS_ANY, general_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { if (switch_event_bind(modname, SWITCH_EVENT_SEND_MESSAGE, SWITCH_EVENT_SUBCLASS_ANY, general_queue_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
} }
if (switch_event_bind(modname, SWITCH_EVENT_SEND_INFO, SWITCH_EVENT_SUBCLASS_ANY, general_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { if (switch_event_bind(modname, SWITCH_EVENT_SEND_INFO, SWITCH_EVENT_SUBCLASS_ANY, general_queue_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
} }
@ -6075,7 +6116,7 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_sofia_shutdown)
switch_event_unbind_callback(sofia_presence_event_handler); switch_event_unbind_callback(sofia_presence_event_handler);
switch_event_unbind_callback(general_event_handler); switch_event_unbind_callback(general_queue_event_handler);
switch_event_unbind_callback(event_handler); switch_event_unbind_callback(event_handler);
switch_queue_push(mod_sofia_globals.presence_queue, NULL); switch_queue_push(mod_sofia_globals.presence_queue, NULL);

View File

@ -97,6 +97,7 @@ typedef struct private_object private_object_t;
#define MY_EVENT_ERROR "sofia::error" #define MY_EVENT_ERROR "sofia::error"
#define MY_EVENT_PROFILE_START "sofia::profile_start" #define MY_EVENT_PROFILE_START "sofia::profile_start"
#define MY_EVENT_NOTIFY_WATCHED_HEADER "sofia::notify_watched_header" #define MY_EVENT_NOTIFY_WATCHED_HEADER "sofia::notify_watched_header"
#define MY_EVENT_WRONG_CALL_STATE "sofia::wrong_call_state"
#define MY_EVENT_TRANSFEROR "sofia::transferor" #define MY_EVENT_TRANSFEROR "sofia::transferor"
#define MY_EVENT_TRANSFEREE "sofia::transferee" #define MY_EVENT_TRANSFEREE "sofia::transferee"
@ -365,6 +366,7 @@ struct mod_sofia_globals {
char hostname[512]; char hostname[512];
switch_queue_t *presence_queue; switch_queue_t *presence_queue;
switch_queue_t *msg_queue; switch_queue_t *msg_queue;
switch_queue_t *general_event_queue;
switch_thread_t *msg_queue_thread[SOFIA_MAX_MSG_QUEUE]; switch_thread_t *msg_queue_thread[SOFIA_MAX_MSG_QUEUE];
int msg_queue_len; int msg_queue_len;
struct sofia_private destroy_private; struct sofia_private destroy_private;
@ -800,6 +802,8 @@ struct private_object {
char *x_freeswitch_support_local; char *x_freeswitch_support_local;
char *last_sent_callee_id_name; char *last_sent_callee_id_name;
char *last_sent_callee_id_number; char *last_sent_callee_id_number;
char *proxy_refer_uuid;
msg_t *proxy_refer_msg;
switch_mutex_t *flag_mutex; switch_mutex_t *flag_mutex;
switch_mutex_t *sofia_mutex; switch_mutex_t *sofia_mutex;
switch_payload_t te; switch_payload_t te;
@ -1193,6 +1197,7 @@ void sofia_glue_fire_events(sofia_profile_t *profile);
void sofia_event_fire(sofia_profile_t *profile, switch_event_t **event); void sofia_event_fire(sofia_profile_t *profile, switch_event_t **event);
void sofia_queue_message(sofia_dispatch_event_t *de); void sofia_queue_message(sofia_dispatch_event_t *de);
int sofia_glue_check_nat(sofia_profile_t *profile, const char *network_ip); int sofia_glue_check_nat(sofia_profile_t *profile, const char *network_ip);
void general_event_handler(switch_event_t *event);
switch_status_t sofia_glue_ext_address_lookup(sofia_profile_t *profile, char **ip, switch_port_t *port, switch_status_t sofia_glue_ext_address_lookup(sofia_profile_t *profile, char **ip, switch_port_t *port,
const char *sourceip, switch_memory_pool_t *pool); const char *sourceip, switch_memory_pool_t *pool);

View File

@ -84,12 +84,30 @@ static void sofia_handle_sip_r_options(switch_core_session_t *session, int statu
nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[]); sofia_dispatch_event_t *de, tagi_t tags[]);
void sofia_handle_sip_r_notify(switch_core_session_t *session, int status, void sofia_handle_sip_r_notify(switch_core_session_t *session, int status,
char const *phrase, char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip, nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[]) sofia_dispatch_event_t *de, tagi_t tags[])
{ {
private_object_t *tech_pvt = switch_core_session_get_private(session);
switch_core_session_t *other_session;
if (tech_pvt->proxy_refer_uuid && (other_session = switch_core_session_locate(tech_pvt->proxy_refer_uuid))) {
switch_core_session_message_t *msg;
msg = switch_core_session_alloc(other_session, sizeof(*msg));
msg->message_id = SWITCH_MESSAGE_INDICATE_RESPOND;
msg->from = __FILE__;
msg->numeric_arg = status;
msg->string_arg = switch_core_session_strdup(other_session, phrase);
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
} else {
tech_pvt->proxy_refer_uuid = NULL;
}
if (status == 481 && sip && !sip->sip_retry_after && sip->sip_call_id && (!sofia_private || !sofia_private->is_call)) { if (status == 481 && sip && !sip->sip_retry_after && sip->sip_call_id && (!sofia_private || !sofia_private->is_call)) {
char *sql; char *sql;
@ -517,6 +535,27 @@ static void sofia_parse_all_invite_headers(sip_t const *sip, switch_core_session
} }
} }
static switch_status_t sofia_pass_notify(switch_core_session_t *session, const char *uuid, const char *payload)
{
switch_core_session_t *other_session;
if ((other_session = switch_core_session_locate(uuid))) {
switch_core_session_message_t *msg;
msg = switch_core_session_alloc(other_session, sizeof(*msg));
MESSAGE_STAMP_FFL(msg);
msg->message_id = SWITCH_MESSAGE_INDICATE_BLIND_TRANSFER_RESPONSE;
msg->string_arg = switch_core_session_strdup(other_session, payload);
msg->from = __FILE__;
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
void sofia_handle_sip_i_notify(switch_core_session_t *session, int status, void sofia_handle_sip_i_notify(switch_core_session_t *session, int status,
char const *phrase, char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip, nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
@ -551,24 +590,21 @@ void sofia_handle_sip_i_notify(switch_core_session_t *session, int status,
} }
if (sofia_test_pflag(profile, PFLAG_PROXY_REFER) && sip->sip_payload && sip->sip_payload->pl_data && if (tech_pvt && tech_pvt->proxy_refer_uuid && sofia_test_pflag(profile, PFLAG_PROXY_REFER) && sip->sip_payload && sip->sip_payload->pl_data &&
sip->sip_content_type && sip->sip_content_type->c_type && sip->sip_content_type && sip->sip_content_type->c_type && switch_stristr("sipfrag", sip->sip_content_type->c_type)) {
switch_stristr("sipfrag", sip->sip_content_type->c_type)) {
switch_core_session_t *other_session; if (sofia_pass_notify(session, tech_pvt->proxy_refer_uuid, sip->sip_payload->pl_data) == SWITCH_STATUS_SUCCESS) {
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { if (tech_pvt->proxy_refer_msg) {
switch_core_session_message_t *msg; msg_ref_destroy(tech_pvt->proxy_refer_msg);
tech_pvt->proxy_refer_msg = NULL;
msg = switch_core_session_alloc(other_session, sizeof(*msg)); }
MESSAGE_STAMP_FFL(msg); tech_pvt->proxy_refer_msg = msg_ref_create(de->data->e_msg);
msg->message_id = SWITCH_MESSAGE_INDICATE_BLIND_TRANSFER_RESPONSE; //nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
msg->string_arg = switch_core_session_strdup(other_session, sip->sip_payload->pl_data); } else {
msg->from = __FILE__; switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
goto end;
} }
goto end;
} }
/* For additional NOTIFY event packages see http://www.iana.org/assignments/sip-events. */ /* For additional NOTIFY event packages see http://www.iana.org/assignments/sip-events. */
@ -1288,6 +1324,34 @@ static void notify_watched_header(switch_core_session_t *session, const char *ms
} }
} }
static void sofia_handle_sip_r_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, switch_core_session_t *session, int status, const char *phrase, sip_t const *sip, sofia_dispatch_event_t *de, tagi_t tags[])
{
private_object_t *tech_pvt = switch_core_session_get_private(session);
switch_core_session_t *other_session;
if (status < 200) {
return;
}
if (tech_pvt->proxy_refer_uuid && (other_session = switch_core_session_locate(tech_pvt->proxy_refer_uuid))) {
switch_core_session_message_t *msg;
msg = switch_core_session_alloc(other_session, sizeof(*msg));
msg->message_id = SWITCH_MESSAGE_INDICATE_RESPOND;
msg->from = __FILE__;
msg->numeric_arg = status;
msg->string_arg = switch_core_session_strdup(other_session, phrase);
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
} else {
tech_pvt->proxy_refer_uuid = NULL;
}
}
//sofia_dispatch_event_t *de //sofia_dispatch_event_t *de
static void our_sofia_event_callback(nua_event_t event, static void our_sofia_event_callback(nua_event_t event,
int status, int status,
@ -1552,7 +1616,9 @@ static void our_sofia_event_callback(nua_event_t event,
sofia_handle_sip_i_bye(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags); sofia_handle_sip_i_bye(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break; break;
case nua_r_notify: case nua_r_notify:
sofia_handle_sip_r_notify(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags); if (session) {
sofia_handle_sip_r_notify(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
}
break; break;
case nua_i_notify: case nua_i_notify:
sofia_handle_sip_i_notify(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags); sofia_handle_sip_i_notify(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
@ -1601,6 +1667,9 @@ static void our_sofia_event_callback(nua_event_t event,
} }
break; break;
case nua_r_refer: case nua_r_refer:
if (session) {
sofia_handle_sip_r_refer(nua, profile, nh, session, status, phrase, sip, de, tags);
}
break; break;
case nua_i_refer: case nua_i_refer:
if (session) { if (session) {
@ -1958,18 +2027,14 @@ void sofia_process_dispatch_event_in_thread(sofia_dispatch_event_t **dep)
sofia_dispatch_event_t *de = *dep; sofia_dispatch_event_t *de = *dep;
switch_memory_pool_t *pool; switch_memory_pool_t *pool;
//sofia_profile_t *profile = (*dep)->profile; //sofia_profile_t *profile = (*dep)->profile;
switch_thread_data_t *td;
switch_core_new_memory_pool(&pool); switch_core_new_memory_pool(&pool);
*dep = NULL; *dep = NULL;
de->pool = pool; de->pool = pool;
td = switch_core_alloc(pool, sizeof(*td));
td->func = sofia_msg_thread_run_once;
td->obj = de;
switch_thread_pool_launch_thread(&td);
} }
@ -2659,64 +2724,88 @@ void *SWITCH_THREAD_FUNC sofia_profile_worker_thread_run(switch_thread_t *thread
uint32_t ireg_loops = profile->ireg_seconds; /* Number of loop iterations done when we haven't checked for registrations */ uint32_t ireg_loops = profile->ireg_seconds; /* Number of loop iterations done when we haven't checked for registrations */
uint32_t iping_loops = profile->iping_freq; /* Number of loop iterations done when we haven't checked for ping expires */ uint32_t iping_loops = profile->iping_freq; /* Number of loop iterations done when we haven't checked for ping expires */
uint32_t gateway_loops = GATEWAY_SECONDS; /* Number of loop iterations done when we haven't checked for gateways */ uint32_t gateway_loops = GATEWAY_SECONDS; /* Number of loop iterations done when we haven't checked for gateways */
void *pop;
int tick = 0, x = 0;
sofia_set_pflag_locked(profile, PFLAG_WORKER_RUNNING); sofia_set_pflag_locked(profile, PFLAG_WORKER_RUNNING);
while ((mod_sofia_globals.running == 1 && sofia_test_pflag(profile, PFLAG_RUNNING))) { while ((mod_sofia_globals.running == 1 && sofia_test_pflag(profile, PFLAG_RUNNING))) {
if (profile->watchdog_enabled) { if (tick) {
uint32_t event_diff = 0, step_diff = 0, event_fail = 0, step_fail = 0; if (profile->watchdog_enabled) {
uint32_t event_diff = 0, step_diff = 0, event_fail = 0, step_fail = 0;
if (profile->step_timeout) { if (profile->step_timeout) {
step_diff = (uint32_t) ((switch_time_now() - profile->last_root_step) / 1000); step_diff = (uint32_t) ((switch_time_now() - profile->last_root_step) / 1000);
if (step_diff > profile->step_timeout) { if (step_diff > profile->step_timeout) {
step_fail = 1; step_fail = 1;
}
}
if (profile->event_timeout) {
event_diff = (uint32_t) ((switch_time_now() - profile->last_sip_event) / 1000);
if (event_diff > profile->event_timeout) {
event_fail = 1;
}
}
if (step_fail && profile->event_timeout && !event_fail) {
step_fail = 0;
}
if (event_fail || step_fail) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile %s: SIP STACK FAILURE DETECTED BY WATCHDOG!\n"
"GOODBYE CRUEL WORLD, I'M LEAVING YOU TODAY....GOODBYE, GOODBYE, GOOD BYE\n", profile->name);
switch_yield(2000000);
watchdog_triggered_abort();
} }
} }
if (profile->event_timeout) {
event_diff = (uint32_t) ((switch_time_now() - profile->last_sip_event) / 1000);
if (event_diff > profile->event_timeout) { if (!sofia_test_pflag(profile, PFLAG_STANDBY)) {
event_fail = 1; if (++ireg_loops >= (uint32_t)profile->ireg_seconds) {
time_t now = switch_epoch_time_now(NULL);
sofia_reg_check_expire(profile, now, 0);
ireg_loops = 0;
} }
}
if (step_fail && profile->event_timeout && !event_fail) {
step_fail = 0;
}
if (event_fail || step_fail) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile %s: SIP STACK FAILURE DETECTED BY WATCHDOG!\n"
"GOODBYE CRUEL WORLD, I'M LEAVING YOU TODAY....GOODBYE, GOODBYE, GOOD BYE\n", profile->name);
switch_yield(2000000);
watchdog_triggered_abort();
}
}
if (!sofia_test_pflag(profile, PFLAG_STANDBY)) {
if (++ireg_loops >= (uint32_t)profile->ireg_seconds) {
time_t now = switch_epoch_time_now(NULL);
sofia_reg_check_expire(profile, now, 0);
ireg_loops = 0;
}
if(++iping_loops >= (uint32_t)profile->iping_freq) { if(++iping_loops >= (uint32_t)profile->iping_freq) {
time_t now = switch_epoch_time_now(NULL); time_t now = switch_epoch_time_now(NULL);
sofia_reg_check_ping_expire(profile, now, profile->iping_seconds); sofia_reg_check_ping_expire(profile, now, profile->iping_seconds);
iping_loops = 0; iping_loops = 0;
}
if (++gateway_loops >= GATEWAY_SECONDS) {
sofia_reg_check_gateway(profile, switch_epoch_time_now(NULL));
sofia_sub_check_gateway(profile, switch_epoch_time_now(NULL));
gateway_loops = 0;
}
} }
if (++gateway_loops >= GATEWAY_SECONDS) { tick = 0;
sofia_reg_check_gateway(profile, switch_epoch_time_now(NULL));
sofia_sub_check_gateway(profile, switch_epoch_time_now(NULL));
gateway_loops = 0;
}
} }
switch_yield(1000000); if (switch_queue_pop_timeout(mod_sofia_globals.general_event_queue, &pop, 100000) == SWITCH_STATUS_SUCCESS) {
do {
switch_event_t *event = (switch_event_t *) pop;
general_event_handler(event);
switch_event_destroy(&event);
pop = NULL;
switch_queue_trypop(mod_sofia_globals.general_event_queue, &pop);
} while (pop);
}
sofia_glue_fire_events(profile);
if (++x == 10) {
tick = 1;
x = 0;
}
} }
@ -6537,9 +6626,9 @@ void *SWITCH_THREAD_FUNC media_on_hold_thread_run(switch_thread_t *thread, void
switch_channel_wait_for_flag(other_channel, CF_MEDIA_ACK, SWITCH_TRUE, 10000, NULL); switch_channel_wait_for_flag(other_channel, CF_MEDIA_ACK, SWITCH_TRUE, 10000, NULL);
if (switch_channel_direction(tech_pvt->channel) == SWITCH_CALL_DIRECTION_INBOUND) { if (switch_channel_direction(tech_pvt->channel) == SWITCH_CALL_DIRECTION_INBOUND) {
switch_ivr_media(switch_core_session_get_uuid(other_session), SMF_REBRIDGE|SMF_REPLYONLY_B); switch_ivr_3p_media(switch_core_session_get_uuid(other_session), SMF_REBRIDGE|SMF_REPLYONLY_B);
} else { } else {
switch_ivr_media(switch_core_session_get_uuid(other_session), SMF_REBRIDGE); switch_ivr_3p_media(switch_core_session_get_uuid(other_session), SMF_REBRIDGE);
} }
@ -7094,6 +7183,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
case nua_callstate_received: case nua_callstate_received:
tech_pvt->recv_invites++; tech_pvt->recv_invites++;
tech_pvt->sent_last_invite = 0; tech_pvt->sent_last_invite = 0;
if (!sofia_test_flag(tech_pvt, TFLAG_SDP)) { if (!sofia_test_flag(tech_pvt, TFLAG_SDP)) {
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session); private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
@ -7113,6 +7203,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
goto done; goto done;
} }
} }
if (r_sdp && !sofia_test_flag(tech_pvt, TFLAG_SDP)) { if (r_sdp && !sofia_test_flag(tech_pvt, TFLAG_SDP)) {
if (switch_channel_test_flag(channel, CF_PROXY_MODE)) { if (switch_channel_test_flag(channel, CF_PROXY_MODE)) {
@ -7342,6 +7433,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
is_t38 = 1; is_t38 = 1;
} }
if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
if ((sofia_test_media_flag(profile, SCMF_DISABLE_HOLD) if ((sofia_test_media_flag(profile, SCMF_DISABLE_HOLD)
|| ((var = switch_channel_get_variable(channel, "rtp_disable_hold")) && switch_true(var))) || ((var = switch_channel_get_variable(channel, "rtp_disable_hold")) && switch_true(var)))
@ -7363,6 +7455,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
int media_on_hold = switch_true(switch_channel_get_variable_dup(channel, "bypass_media_resume_on_hold", SWITCH_FALSE, -1)); int media_on_hold = switch_true(switch_channel_get_variable_dup(channel, "bypass_media_resume_on_hold", SWITCH_FALSE, -1));
switch_core_media_clear_rtp_flag(other_session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_AUTOADJ); switch_core_media_clear_rtp_flag(other_session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_AUTOADJ);
if (switch_channel_test_flag(channel, CF_PROXY_MODE) && !is_t38 && if (switch_channel_test_flag(channel, CF_PROXY_MODE) && !is_t38 &&
((profile->media_options & MEDIA_OPT_MEDIA_ON_HOLD) || media_on_hold)) { ((profile->media_options & MEDIA_OPT_MEDIA_ON_HOLD) || media_on_hold)) {
@ -7374,7 +7467,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
switch_core_media_clear_rtp_flag(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_AUTOADJ); switch_core_media_clear_rtp_flag(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_AUTOADJ);
if (!switch_channel_media_ready(channel)) { if (!switch_channel_media_ready(channel)) {
if (switch_channel_direction(tech_pvt->channel) == SWITCH_CALL_DIRECTION_INBOUND) { //if (switch_channel_direction(tech_pvt->channel) == SWITCH_CALL_DIRECTION_INBOUND) {
//const char *r_sdp = switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE); //const char *r_sdp = switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE);
@ -7383,9 +7476,10 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR"); switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR");
status = SWITCH_STATUS_FALSE; status = SWITCH_STATUS_FALSE;
switch_core_session_rwunlock(other_session); switch_core_session_rwunlock(other_session);
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
goto done; goto done;
} }
} //}
} }
@ -7397,6 +7491,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
goto done; goto done;
} }
} }
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 1); switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 1);
if (sofia_use_soa(tech_pvt)) { if (sofia_use_soa(tech_pvt)) {
@ -7431,8 +7526,9 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
} }
} }
other_tech_pvt = switch_core_session_get_private(other_session); other_tech_pvt = switch_core_session_get_private(other_session);
if(sofia_test_flag(other_tech_pvt, TFLAG_REINVITED)) {
if (sofia_test_flag(other_tech_pvt, TFLAG_REINVITED)) {
/* The other leg won the reinvite race */ /* The other leg won the reinvite race */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Other leg already handling reinvite, so responding with 491\n"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Other leg already handling reinvite, so responding with 491\n");
nua_respond(tech_pvt->nh, SIP_491_REQUEST_PENDING, TAG_END()); nua_respond(tech_pvt->nh, SIP_491_REQUEST_PENDING, TAG_END());
@ -7528,25 +7624,18 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
} }
} }
if (is_ok) { if (is_ok) {
if (switch_core_session_local_crypto_key(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) { if (switch_core_session_local_crypto_key(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) {
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 0); switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 0);
} }
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh, SIP_200_OK, nua_respond(tech_pvt->nh, SIP_200_OK,
SIPTAG_CONTACT_STR(tech_pvt->reply_contact), NUTAG_MEDIA_ENABLE(0),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str), SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SOATAG_REUSE_REJECTED(1), SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(tech_pvt->mparams.local_sdp_str), TAG_END());
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)), TAG_END());
} else {
nua_respond(tech_pvt->nh, SIP_200_OK,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(tech_pvt->mparams.local_sdp_str), TAG_END());
}
if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_REINVITE) == SWITCH_STATUS_SUCCESS) { if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_REINVITE) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(session)); switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(session));
switch_event_fire(&s_event); switch_event_fire(&s_event);
@ -7962,6 +8051,29 @@ nua_handle_t *sofia_global_nua_handle_by_replaces(sip_replaces_t *replaces)
} }
static switch_status_t sofia_process_proxy_refer(switch_core_session_t *session, const char *refer_to)
{
switch_core_session_t *other_session;
private_object_t *tech_pvt = switch_core_session_get_private(session);
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
switch_core_session_message_t *msg;
tech_pvt->proxy_refer_uuid = switch_core_session_strdup(session, switch_core_session_get_uuid(other_session));
msg = switch_core_session_alloc(other_session, sizeof(*msg));
MESSAGE_STAMP_FFL(msg);
msg->message_id = SWITCH_MESSAGE_INDICATE_DEFLECT;
msg->string_arg = switch_core_session_strdup(other_session, refer_to);
msg->string_array_arg[0] = switch_core_session_strdup(other_session, switch_core_session_get_uuid(session));
msg->from = __FILE__;
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, switch_core_session_t *session, sip_t const *sip, void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, switch_core_session_t *session, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[]) sofia_dispatch_event_t *de, tagi_t tags[])
{ {
@ -7994,27 +8106,18 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t
full_ref_to = sip_header_as_string(home, (void *) sip->sip_refer_to); full_ref_to = sip_header_as_string(home, (void *) sip->sip_refer_to);
} }
if (full_ref_to && sofia_test_pflag(profile, PFLAG_PROXY_REFER)) {
if (sofia_test_pflag(profile, PFLAG_PROXY_REFER)) { if (sofia_process_proxy_refer(session, full_ref_to) == SWITCH_STATUS_SUCCESS) {
switch_core_session_t *other_session; if (tech_pvt->proxy_refer_msg) {
msg_ref_destroy(tech_pvt->proxy_refer_msg);
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { tech_pvt->proxy_refer_msg = NULL;
switch_core_session_message_t *msg; }
tech_pvt->proxy_refer_msg = msg_ref_create(de->data->e_msg);
msg = switch_core_session_alloc(other_session, sizeof(*msg)); //nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_EXPIRES_STR("60"), TAG_END());
MESSAGE_STAMP_FFL(msg);
msg->message_id = SWITCH_MESSAGE_INDICATE_DEFLECT;
msg->string_arg = switch_core_session_strdup(other_session, full_ref_to);
msg->from = __FILE__;
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_EXPIRES_STR("60"), TAG_END());
goto done; goto done;
} }
} }
from = sip->sip_from; from = sip->sip_from;
//to = sip->sip_to; //to = sip->sip_to;
@ -9104,6 +9207,11 @@ void sofia_handle_sip_i_reinvite(switch_core_session_t *session,
if (session) { if (session) {
channel = switch_core_session_get_channel(session); channel = switch_core_session_get_channel(session);
tech_pvt = switch_core_session_get_private(session); tech_pvt = switch_core_session_get_private(session);
if (sip->sip_payload && sip->sip_payload->pl_data) {
tech_pvt->mparams.last_sdp_str = switch_core_session_strdup(session, sip->sip_payload->pl_data);
}
} }
if (session && profile && sip && sofia_test_pflag(profile, PFLAG_TRACK_CALLS)) { if (session && profile && sip && sofia_test_pflag(profile, PFLAG_TRACK_CALLS)) {
@ -9467,6 +9575,10 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia
} }
} }
tech_pvt->mparams.remote_ip = switch_core_session_strdup(session, network_ip);
tech_pvt->mparams.remote_port = network_port;
if (!is_auth && if (!is_auth &&
(sofia_test_pflag(profile, PFLAG_AUTH_CALLS) (sofia_test_pflag(profile, PFLAG_AUTH_CALLS)
|| (!sofia_test_pflag(profile, PFLAG_BLIND_AUTH) && (sip->sip_proxy_authorization || sip->sip_authorization)))) { || (!sofia_test_pflag(profile, PFLAG_BLIND_AUTH) && (sip->sip_proxy_authorization || sip->sip_authorization)))) {
@ -9493,11 +9605,6 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia
is_auth++; is_auth++;
} }
tech_pvt->mparams.remote_ip = switch_core_session_strdup(session, network_ip);
tech_pvt->mparams.remote_port = network_port;
channel = tech_pvt->channel = switch_core_session_get_channel(session); channel = tech_pvt->channel = switch_core_session_get_channel(session);
switch_channel_set_variable_printf(channel, "sip_local_network_addr", "%s", profile->extsipip ? profile->extsipip : profile->sipip); switch_channel_set_variable_printf(channel, "sip_local_network_addr", "%s", profile->extsipip ? profile->extsipip : profile->sipip);

View File

@ -2476,9 +2476,6 @@ switch_bool_t sofia_glue_execute_sql_callback(sofia_profile_t *profile,
switch_cache_db_release_db_handle(&dbh); switch_cache_db_release_db_handle(&dbh);
sofia_glue_fire_events(profile);
return ret; return ret;
} }
@ -2515,9 +2512,6 @@ char *sofia_glue_execute_sql2str(sofia_profile_t *profile, switch_mutex_t *mutex
switch_cache_db_release_db_handle(&dbh); switch_cache_db_release_db_handle(&dbh);
sofia_glue_fire_events(profile);
return ret; return ret;
} }

View File

@ -1447,14 +1447,29 @@ uint8_t sofia_reg_handle_register_token(nua_t *nua, sofia_profile_t *profile, nu
if (sip->sip_path) { if (sip->sip_path) {
if ((path_val = sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_path))) { char *path_stripped = NULL;
char *path_stripped = sofia_glue_get_url_from_contact(path_val, SWITCH_TRUE); char *path_val_to_encode = NULL;
su_free(nua_handle_home(nh), path_val); su_strlst_t *path_list = su_strlst_create(nua_handle_home(nh));
path_val = path_stripped; sip_path_t *next_path = sip->sip_path;
path_encoded_len = (int)(strlen(path_val) * 3) + 1; for (; next_path; next_path = next_path->r_next) {
path_val = sip_header_as_string(nua_handle_home(nh), (void *) next_path);
if (path_val) {
path_stripped = sofia_glue_get_url_from_contact(path_val, SWITCH_TRUE);
su_free(nua_handle_home(nh), path_val);
su_strlst_dup_append(path_list, path_stripped);
switch_safe_free(path_stripped);
}
}
path_val = su_strlst_join(path_list, nua_handle_home(nh), ",");
path_val_to_encode = su_strlst_join(path_list, nua_handle_home(nh), "%2C");
su_strlst_destroy(path_list);
if (path_val_to_encode) {
path_encoded_len = (int)(strlen(path_val_to_encode) * 3) + 1;
switch_zmalloc(path_encoded, path_encoded_len); switch_zmalloc(path_encoded, path_encoded_len);
switch_copy_string(path_encoded, ";fs_path=", 10); switch_copy_string(path_encoded, ";fs_path=", 10);
switch_url_encode(path_val, path_encoded + 9, path_encoded_len - 9); switch_url_encode(path_val_to_encode, path_encoded + 9, path_encoded_len - 9);
su_free(nua_handle_home(nh), path_val_to_encode);
} }
} else if (is_nat) { } else if (is_nat) {
char my_contact_str[1024]; char my_contact_str[1024];
@ -2195,7 +2210,7 @@ uint8_t sofia_reg_handle_register_token(nua_t *nua, sofia_profile_t *profile, nu
switch_safe_free(display_m); switch_safe_free(display_m);
switch_safe_free(dup_mwi_account); switch_safe_free(dup_mwi_account);
switch_safe_free(utmp); switch_safe_free(utmp);
switch_safe_free(path_val); su_free(nua_handle_home(nh), path_val);
switch_safe_free(token_val); switch_safe_free(token_val);
if (auth_params) { if (auth_params) {

Some files were not shown because too many files have changed in this diff Show More