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)
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
@cd libs/libvpx && $(MAKE)

View File

@ -1,8 +1,8 @@
<configuration name="httapi.conf" description="HT-TAPI Hypertext Telephony API">
<settings>
<!-- print xml on the consol -->
<!-- print xml on the console -->
<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"/>
<!-- how often to re-check the server to make sure the remote file has not changed -->
<param name="file-cache-ttl" value="300"/>

View File

@ -31,7 +31,9 @@ case "$1" in
done
if [ ! -d "/etc/freeswitch" ]; then
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
if [ ! -d "/etc/freeswitch/tls" ]; then
mkdir -p /etc/freeswitch/tls/

View File

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

View File

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

View File

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

View File

@ -43,7 +43,9 @@
"jquery-json": "~2.5.1",
"datatables": "~1.10.8",
"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": {
"angular": "~1.3.15",

View File

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

View File

@ -143,7 +143,7 @@ button.btn i {
-webkit-animation: rotator 1.4s linear infinite;
animation: rotator 1.4s linear infinite;
position: absolute;
top: 35%;
top: 50%;
left: 50%;
margin-left: -35px;
zoom: 2;
@ -503,17 +503,17 @@ body .modal-body .btn-group .btn.active {
}*/
#incall .video-call {
width: 81vw;
height: 82vw;
max-height: calc(100% - 1vw);
max-width: 146.78vh;
margin: auto;
margin: 7px auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin-top: auto;
padding: 1px;
}
#incall .avatar {
@ -764,7 +764,7 @@ body .modal-body .btn-group .btn.active {
}
#sidebar-wrapper {
z-index: 1000;
z-index: 8;
position: fixed;
width: 0;
height: calc(100% - 60px);
@ -1686,3 +1686,53 @@ body:-webkit-full-screen #incall .video-footer {
from { max-height:2000px;}
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>
<div ng-include="'partials/menu.html'"></div>
<div ng-include="'partials/settings.html'"></div>
<div id="wrapper" class="toggled">
<!-- Sidebar -->
@ -96,6 +97,8 @@
<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/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 -->
<!-- endbuild -->
@ -125,7 +128,7 @@
<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/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/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'">
<li role="presentation" ng-class="{'active': activePane == 'members'}">
<a ng-click="activePane = 'members'" href="">
Members
{{ 'CHAT_TITLE_MEMBERS' | translate}}
</a>
</li>
<li role="presentation" ng-class="{'active': activePane == 'chat'}">
<a ng-click="activePane = 'chat'" href="">
Chat
{{ 'CHAT_TITLE' | translate }}
</a>
</li>
</ul>
<div class="chat-members" ng-show="activePane == 'members'">
<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 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>
<div class="col-md-6 ctrl-section">
<h3>General</h3>
<h3>{{ 'CHAT_GENERAL' | translate }}</h3>
<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>
Kick
{{ 'CHAT_KICK' | translate }}
</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>
Floor
{{ 'CHAT_FLOOR' | translate }}
</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>
<span style="margin-left: -9px">Transfer</span>
<span style="margin-left: -9px">{{ 'CHAT_TRANSFER' | translate }}</span>
</a>
</div>
</div>
<div class="col-md-6 ctrl-section">
<h3>Banner</h3>
<h3>{{ 'CHAT_BANNER' | translate }}</h3>
<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>
Set
{{ 'CHAT_SET' | translate }}
</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>
Reset
{{ 'CHAT_RESET' | translate }}
</a>
</div>
</div>
@ -90,81 +90,81 @@
<div>
<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">
<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>
Prev
{{ 'CHAT_PREV' | translate }}
</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>
Id
</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>
Next
{{ 'CHAT_NEXT' | translate }}
</a>
</div>
<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>
Prev
{{ 'CHAT_PREV' | translate }}
</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>
Id
</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>
Next
{{ 'CHAT_NEXT' | translate }}
</a>
</div>
<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>
Prev
{{ 'CHAT_PREV' | translate }}
</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>
Id
</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>
Next
{{ 'CHAT_NEXT' | translate }}
</a>
</div>
</div>
<div class="col-md-6 ctrl-section">
<h3>Audio/Video</h3>
<h3>{{ 'CHAT_AUDIO_VIDEO' | translate }}</h3>
<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>
{{ member.status.audio.muted ? 'Unmute' : 'Mute' }}
{{ member.status.audio.muted ? 'CHAT_UNMUTE_MIC' : 'CHAT_MUTE_MIC' | translate }}
</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>
{{ member.status.video.muted ? 'Unmute' : 'Mute' }}
{{ member.status.video.muted ? 'CHAT_UNMUTE_MIC' : 'CHAT_MUTE_MIC' | translate }}
</a>
</div>
<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>
Vol
{{ 'CHAT_VOL_MINUS' | translate }}
</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>
Vol +
{{ 'CHAT_VOL_PLUS' | translate }}
</a>
</div>
<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>
Gain
{{ 'CHAT_GAIN_MINUS' | translate }}
</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>
Gain +
{{ 'CHAT_GAIN_PLUS' | translate }}
</a>
</div>
</div>
@ -176,7 +176,7 @@
<div class="chat-history" ng-show="activePane == 'chat'">
<div class="chat-messages">
<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 class="chat-message" ng-repeat="message in messages" title="Sent at {{ message.created_at|date }}.">
<div class="chat-message-metadata">{{ message.from }}:</div>
@ -188,9 +188,9 @@
<div class="chat-message-input">
<form ng-submit="send()" >
<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">
Send
{{ 'CHAT_SEND_MESSAGE' | translate }}
<span class="mdi-navigation-arrow-forward chat-message-input-group-icon-button"></span>
</button>
</div>

View File

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

View File

@ -1,6 +1,15 @@
<div ng-show="loading">
<h3 style="margin-top: 4%;" class="text-center">Calling to {{ dialpadNumber }}...</h3>
<svg class="spinner" width="65px" height="65px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<h2 ng-show="cancelled" style="margin-top: 4%;" class="text-center">{{ 'CANCELLING' | translate}}</h2>
<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>
</svg>
</div>
@ -10,7 +19,7 @@
<div class="panel-heading">
<div class="panel-title">
<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 class="pull-right pull-right-margin dropdown">
@ -18,14 +27,14 @@
<i class="mdi-navigation-more-vert"></i>
</a>
<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>
</span>
</div>
</div>
<ul class="call-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>
<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>
</a>
</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">
<a href="" ng-click="backspace()">
<i class="mdi-content-backspace"></i>
@ -154,7 +163,7 @@
</div>
</div>
<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>
</button>
</div>

View File

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

View File

@ -2,13 +2,13 @@
<div class="container-fluid">
<div class="https-untrusted navbar-center" ng-show="!safeProtocol">
<div class="https-message">
This Connection is Untrusted.
{{ 'MESSAGE_CONNECTION_UNTRUSTED' | translate }}
</div>
</div>
<div class="navbar-header">
<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>
@ -25,10 +25,10 @@
<span class="caret"></span>
</a>
<ul class="dropdown-menu drop-net-info" role="menu" ng-click="$event.stopPropagation()">
<li><a class="title">Bandwidth Info</a></li>
<li><a>Outgoing: {{bandUp}} Kbps</a></li>
<li><a>Incoming: {{bandDown}} Kbps</a></li>
<li><a>Video Resolution: {{vidRes}}</a></li>
<li><a class="title">{{ 'BANDWIDTH_INFO' | translate }}</a></li>
<li><a>{{ 'BANDWIDTH_INFO_OUTGOING' | translate }} {{bandUp}} Kbps</a></li>
<li><a>{{ 'BANDWIDTH_INFO_INCOMING' | translate }} {{bandDown}} Kbps</a></li>
<li><a>{{ 'BANDWIDTH_INFO_VIDEO_RES' | translate }} {{vidRes}}</a></li>
</ul>
</li>
<li>
@ -38,11 +38,11 @@
</li>
<li>
<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>
</li>
<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>
</a>
</li>
@ -63,10 +63,10 @@
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="#/?sessid=random" target="_blank">Open New Window</a></li>
<li><a href="" ng-click="openModal('partials/modal_logininfo.html', 'ModalLoginInformationController')">Change Login Information</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_INFO' | translate}}</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>
</li>
<li class="navbar-item-icon">
@ -75,9 +75,9 @@
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="" ng-click="showAbout()">About</a></li>
<li><a href="" ng-click="showContributors()">Contributors</a></li>
<li><a href="https://freeswitch.org/confluence/x/MQCT" target="_blank">Help</a></li>
<li><a href="" ng-click="showAbout()">{{ 'ABOUT' | translate}}</a></li>
<li><a href="" ng-click="showContributors()">{{ 'CONTRIBUTORS' | translate}}</a></li>
<li><a href="https://freeswitch.org/confluence/x/MQCT" target="_blank">{{ 'HELP' | translate}}</a></li>
</ul>
</li>

View File

@ -1,35 +1,35 @@
<div class="modal-header">
<h3 class="modal-title">Login Information</h3>
<h3 class="modal-title">{{ 'LOGIN_INFORMATION' | translate}}</h3>
</div>
<div class="modal-body">
<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"/>
</div>
<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"/>
</div>
<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">
</div>
<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">
</div>
<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">
</div>
</div>
<div class="modal-footer">
<!-- <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>

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="panel panel-material-blue-900 shadow-z-2 ">
<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 class="panel-body">
<div class="preview-wrapper">
@ -20,13 +20,13 @@
</div>
<form name="form">
<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"
ng-options="item.id as item.label for item in verto.data.videoDevices" ng-change="localVideo()" >
</select>
</div>
<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"
ng-options="item.id as item.label for item in verto.data.audioDevices" ng-change="localVideo()">
</select>
@ -36,8 +36,8 @@
</a>
<div class="form-group text-center">
<button type="submit" class="btn btn-success" ng-click="endPreview()" title="Save">
Save
<button type="submit" class="btn btn-success" ng-click="endPreview()" title="{{ 'SAVE' | translate }}">
{{ 'SAVE' | translate }}
</button>
</div>
</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="card">
<div class="card-body text-center">
<h2>Loading</h2>
<h2>{{ 'LOADING' | translate }}</h2>
<div class="progress progress-striped active">
<div class="progress-bar" ng-class="{'progress-bar-danger': interrupt_next}" style="width: {{ progress_percentage }}%"></div>
</div>
<div ng-bind="message"></div>
<div class="splash-errors" ng-if="errors.length">
<h4>Errors</h4>
<h4>{{ 'ERRORS' | translate }}</h4>
<ul ng-repeat="error in errors">
<li>{{ ::error }}</li>
</ul>

View File

@ -2,28 +2,28 @@
<div class="video-wrapper">
<div class="video-hover-buttons" ng-show="verto.data.callState == 'active' && !watcher">
<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()">
<i class="mdi-av-play-circle-outline"></i>
</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()">
<i class="mdi-av-stop"></i>
</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()">
<i class="mdi-toggle-radio-button-on"></i>
</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()">
<i class="mdi-image-switch-camera"></i>
</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()">
<i class="mdi-image-photo-camera"></i>
</button>
<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"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="mdi-action-view-module"></i>
@ -37,28 +37,28 @@
</div>
</div>
<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)">
<i class="" ng-class="{'mdi-av-mic': !verto.data.mutedMic, 'mdi-av-mic-off': verto.data.mutedMic}"></i>
</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">
<i class="" ng-class="{'mdi-av-videocam': !verto.data.mutedVideo, 'mdi-av-videocam-off': verto.data.mutedVideo}"></i>
</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()">
<i class="" ng-class="{'mdi-navigation-fullscreen': !fullscreenEnabled, 'mdi-navigation-fullscreen-exit': fullscreenEnabled}"></i>
</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()">
<i class="mdi-hardware-desktop-windows"></i>
</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">
<i class="mdi-communication-chat"></i>
</button>
<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>
<span class="caret"></span>
</button>
@ -69,7 +69,7 @@
</ul>
</div>
<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>
<span class="caret"></span>
</button>
@ -96,9 +96,9 @@
</div>
</div>
<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>
{{ watcher ? 'Close' : 'End Call' }}
{{ watcher ? 'BUTTON_CLOSE' : 'BUTTON_END_CALL' }}
</button>
</div>
</div>

View File

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

View File

@ -17,10 +17,11 @@
'ui.gravatar',
'ui.bootstrap',
'directive.g+signin',
'pascalprecht.translate',
]);
vertoApp.config(['$routeProvider', 'gravatarServiceProvider',
function($routeProvider, gravatarServiceProvider) {
vertoApp.config(['$routeProvider', 'gravatarServiceProvider', '$translateProvider',
function($routeProvider, gravatarServiceProvider, $translateProvider) {
$routeProvider.
when('/', {
title: 'Loading',
@ -59,6 +60,47 @@
gravatarServiceProvider.defaults = {
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
.module('vertoControllers')
.controller('ChatController', ['$scope', '$rootScope', '$http',
'$location', '$anchorScroll', '$timeout', 'verto', 'prompt',
'$location', '$anchorScroll', '$timeout', 'verto', 'prompt', '$translate',
function($scope, $rootScope, $http, $location, $anchorScroll, $timeout,
verto, prompt) {
verto, prompt, $translate) {
console.debug('Executing ChatController.');
function scrollToChatBottom() {
@ -246,7 +246,7 @@
console.log('$scope.confBanner');
prompt({
title: 'Please insert the banner text',
title: $translate.instant('TITLE_INSERT_BANNER'),
input: true,
label: '',
value: '',
@ -263,7 +263,7 @@
return;
}
shortPrompt('Please insert the Canvas Id', function(canvasID) {
shortPrompt($translate.instant('TITLE_INSERT_CANVAS_ID'), function(canvasID) {
console.log(memberID, canvasID);
verto.setCanvasIn(memberID, canvasID);
});
@ -276,7 +276,7 @@
return;
}
shortPrompt('Please insert the Canvas Id', function(canvasID) {
shortPrompt($translate.instant('TITLE_INSERT_CANVAS_ID'), function(canvasID) {
verto.setCanvasOut(memberID, canvasID);
});
};
@ -287,7 +287,7 @@
return;
}
shortPrompt('Please insert the Layer', function(canvasID) {
shortPrompt($translate.instant('TITLE_INSERT_LAYER'), function(canvasID) {
verto.setLayer(memberID, canvasID);
});
};
@ -321,10 +321,10 @@
$scope.confTransfer = function(memberID) {
console.log('$scope.confTransfer');
prompt({
title: 'Transfer party?',
message: 'To what destination would you like to transfer this call?',
title: $translate.instant('TITLE_TRANSFER'),
message: $translate.instant('MESSAGE_TRANSFER'),
input: true,
label: 'Destination',
label: $translate.instant('LABEL_TRANSFER'),
value: '',
}).then(function(exten) {
if (exten) {

View File

@ -89,7 +89,17 @@
if (extension.indexOf('-canvas-') != -1) {
$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');
return;
}
@ -109,6 +119,7 @@
* Call to the number in the $rootScope.dialpadNumber.
*/
$scope.loading = false;
$scope.cancelled = false;
$rootScope.call = function(extension) {
if (!storage.data.testSpeedJoin || !$rootScope.dialpadNumber) {
return call(extension);
@ -116,10 +127,19 @@
$scope.loading = true;
verto.testSpeed(function() {
$scope.loading = false;
call(extension);
if ($scope.cancelled) {
$scope.cancelled = false;
$scope.loading = false;
return;
} else {
call(extension);
}
});
}
$rootScope.cancel = function() {
$scope.cancelled = true;
}
}
]);

View File

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

View File

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

View File

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

View File

@ -3,16 +3,25 @@
angular
.module('vertoControllers')
.controller('ModalSettingsController', ['$scope', '$http',
'$location', '$modalInstance', '$rootScope', 'storage', 'verto',
function($scope, $http, $location, $modalInstance, $rootScope, storage, verto) {
.controller('SettingsController', ['$scope', '$http',
'$location', '$rootScope', 'storage', 'verto', '$translate', 'toastr',
function($scope, $http, $location, $rootScope, storage, verto, $translate, toastr) {
console.debug('Executing ModalSettingsController.');
$.material.init();
$scope.speakerFeature = typeof document.getElementById('webcam').sinkId !== 'undefined';
$scope.storage = storage;
$scope.verto = verto;
$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() {
if ($scope.mydata.selectedSpeaker != storage.data.selectedSpeaker) {
@ -24,17 +33,24 @@
if (storage.data.autoBand) {
$scope.testSpeed();
}
$modalInstance.close('Ok.');
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
$scope.refreshDeviceList = function() {
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() {
return verto.testSpeed(cb);
@ -49,7 +65,6 @@
if (confirm('Factory Reset Settings?')) {
storage.factoryReset();
$scope.logout();
$modalInstance.close('Ok.');
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;}
if(elements[i]!==payload)newLine[index++]=elements[i];}
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';}
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;}
@ -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++;}
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';}
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-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("vid-write-png",null,file);};$.verto.conf.prototype.setVideoLayout=function(layout,canvasID){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("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-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>"+
(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);}});}
$("#"+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>";}
@ -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;}
if(!dialog.cause){dialog.cause="NORMAL CLEARING";}
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",{});}
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;}

View File

@ -799,6 +799,7 @@ function docall() {
check_vid_res();
console.error(outgoingBandwidth, incomingBandwidth);
cur_call = vertoHandle.newCall({
destination_number: $("#ext").val(),
caller_id_name: $("#cidname").val(),
@ -807,9 +808,9 @@ function docall() {
incomingBandwidth: incomingBandwidth,
useVideo: check_vid(),
useStereo: $("#use_stereo").is(':checked'),
useCamera: sessid ? "none" : $("#usecamera").find(":selected").val(),
useMic: $("#usemic").find(":selected").val(),
useSpeak: $("#usespeak").find(":selected").val(),
useCamera: (sessid || canvas_id) ? "none" : $("#usecamera").find(":selected").val(),
useMic: (sessid || canvas_id) ? "none" : $("#usemic").find(":selected").val(),
useSpeak: (sessid || canvas_id) ? "none" : $("#usespeak").find(":selected").val(),
dedEnc: $("#use_dedenc").is(':checked'),
mirrorInput: $("#mirror_input").is(':checked'),
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);
if (chan_priv->peerhangup) {
/* Call is inbound and hangup has been initiated by peer */
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);
}
ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
}
}
}

View File

@ -4,7 +4,6 @@
#
########################################################
# TODO: FreeSWITCH AutoStart
# TODO: Install on Raspbian
# TODO: Allow Selection of Source or Package Install on Debian
DIALOG=${DIALOG=dialog}
@ -17,7 +16,18 @@ install_prereqs() {
#install the prereqs
echo "Making sure we have the prereqs for this script to run. Please Stand by..."
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() {
@ -101,7 +111,7 @@ is_private_ip() {
}
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 \
--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() {
echo "REPO = $REPO"
if [ ! -d /usr/src/freeswitch.git ]; then
cd /usr/src
git clone $REPO freeswitch.git
else
cd /usr/src/freeswitch.git
git clean -fdx
git reset -hard origin/$FS_REV
git reset --hard origin/$FS_REV
git pull
fi
}
@ -164,7 +175,7 @@ install_certs() {
NEED_CERTS_INSTALL=0
else
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
else
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}/*
fi
cd /usr/src/freeswitch.git
./bootstrap.sh -j
if [ ! -d /usr/src/freeswitch.git/configure ]; then
./bootstrap.sh -j
fi
./configure -C
make -j$JLIMIT 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 \
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
build_fs
}
@ -247,11 +261,12 @@ fs_ver_select
get_network_settings
if [ "$ID" = "debian" ]; then
## These only work on Jessie at this time
config_fs_repos
freeswitch_debian_source
elif [ "$ID" = "raspbian" ]; then
#freeswitch_raspbiani123
JLIMIT="3"
freeswitch_raspbian_source
fi
install_vc
@ -264,10 +279,10 @@ if [ "x$PRIVIP" != "x$IPADDR" ]; then
elif [ $VIPFQDN -eq 1 ]; then
echo "Skipping LetsEncrypt\n"
else
get_dletsencrypt
get_letsencrypt
install_certs
fi
else
echo "Skipping LetsEncrypt. Since we are on Private IP Space";
echo "Skipping LetsEncrypt. Since we are on a Private IP Address";
fi

View File

@ -42,6 +42,7 @@ SWITCH_BEGIN_EXTERN_C
#define SWITCH_NO_CRYPTO_TAG -1
typedef enum {
DTMF_AUTO,
DTMF_2833,
DTMF_INFO,
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_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);
/*!\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

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_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.
\param uuid the uuid of the session to hold

View File

@ -326,6 +326,7 @@ typedef struct switch_mm_s {
int vbuf;
switch_video_profile_t vprofile;
switch_video_encode_speed_t vencspd;
uint8_t try_hardware_encoder;
} switch_mm_t;
/*! an abstract representation of a file handle (some parameters based on compat with libsndfile) */
@ -393,6 +394,10 @@ struct switch_file_handle {
char *modname;
switch_mm_t mm;
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 */
@ -633,6 +638,7 @@ struct switch_video_codec_settings {
uint32_t bandwidth;
int32_t width;
int32_t height;
uint8_t try_hardware_encoder;
};
union switch_codec_settings {

View File

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

View File

@ -27,7 +27,7 @@
* Anthony Minessale <anthm@freeswitch.org>
* 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 *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;
}
@ -150,8 +154,7 @@ typedef struct h263_state_s {
int quant;
} h263_state_t;
typedef struct our_h264_nalu_s
{
typedef struct our_h264_nalu_s {
const uint8_t *start;
const uint8_t *eat;
uint32_t len;
@ -181,9 +184,11 @@ typedef struct h264_codec_context_s {
AVCodecContext *encoder_ctx;
AVFrame *encoder_avframe;
AVPacket encoder_avpacket;
AVFrame *decoder_avframe;
our_h264_nalu_t nalus[MAX_NALUS];
enum AVCodecID av_codec_id;
uint16_t last_seq; // last received frame->seq
int hw_encoder;
} h264_codec_context_t;
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;
}
#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)
{
#ifndef H263_MODE_B
uint8_t *d = data;
uint32_t code = (ntohl(*(uint32_t *)data) & 0xFFFFFC00) >> 10;
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===============" : "");
#endif
#endif
}
#endif
const uint8_t *fs_h263_find_resync_marker_reverse(const uint8_t *restrict start,
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));
}
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)
{
AVPacket *pkt = &context->encoder_avpacket;
our_h264_nalu_t *nalu = &context->nalus[context->nalu_current_index];
if (!nalu->len) {
frame->datalen = 0;
frame->m = 0;
if (context->encoder_avpacket.size > 0) av_free_packet(&context->encoder_avpacket);
// if (context->encoder_avframe->data[0]) av_freep(&context->encoder_avframe->data[0]);
if (pkt->size > 0) av_packet_unref(pkt);
context->nalu_current_index = 0;
return SWITCH_STATUS_NOTFOUND;
}
if (context->av_codec_id == AV_CODEC_ID_H263) {
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);
}
assert(nalu->len);
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;
}
}
return consume_h264_bitstream(context, frame);
}
static switch_status_t open_encoder(h264_codec_context_t *context, uint32_t width, uint32_t height)
{
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) {
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->rc_max_rate = context->bandwidth * 1024;
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) {
#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;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
context->encoder_ctx->rc_min_rate = context->encoder_ctx->rc_max_rate;
context->encoder_ctx->opaque = context;
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) {
context->encoder_ctx->profile = FF_PROFILE_H264_BASELINE;
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
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
// libx264-medium.ffpreset preset
context->encoder_ctx->me_range = 16; // me_range=16
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->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop
context->encoder_ctx->me_cmp|= 1; // cmp=+chroma, where CHROMA = 1
context->encoder_ctx->me_range = 21; // me_range=16
context->encoder_ctx->max_b_frames = 3; // bf=3
//context->encoder_ctx->refs = 3; // refs=3
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) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open codec\n");
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)
{
int encoding, decoding;
h264_codec_context_t *context = NULL;
encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
if (!(encoding || decoding)) {
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:
// todo, do some clean up
return SWITCH_STATUS_FALSE;
@ -990,22 +1026,9 @@ error:
static void __attribute__((unused)) fill_avframe(AVFrame *pict, switch_image_t *img)
{
int i;
uint8_t *y = img->planes[0];
uint8_t *u = img->planes[1];
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);
}
switch_I420_copy2(img->planes, img->stride,
pict->data, pict->linesize,
img->d_w, img->d_h);
}
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;
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;
}
@ -1149,7 +1173,10 @@ static switch_status_t switch_h264_encode(switch_codec_t *codec, switch_frame_t
*got_output = 0;
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
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;
return consume_nalu(context, frame);
} 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);
context->nalu_current_index = 0;
return consume_nalu(context, frame);
} 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 */
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) {
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_peek_zerocopy(context->nalu_buffer, (const void **)&pkt.data);
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);
// 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) {
int width = avctx->width;
int height = avctx->height;
int i;
int width = picture->width;
int height = picture->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);
assert(context->img);
switch_assert(context->img);
}
#if 0
context->img->w = picture->linesize[0];
context->img->h = picture->linesize[1];
context->img->d_w = width;
context->img->d_h = height;
//context->img->planes[0] = picture->data[0];
//context->img->planes[1] = picture->data[1];
//context->img->planes[2] = picture->data[2];
//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);
}
#endif
switch_I420_copy2(picture->data, picture->linesize,
context->img->planes, context->img->stride,
width, height);
frame->img = context->img;
}
av_frame_free(&picture);
av_free_packet(&pkt);
av_frame_unref(picture);
}
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);
}
if (context->decoder_avframe) {
av_frame_free(&context->decoder_avframe);
}
return SWITCH_STATUS_SUCCESS;
}
@ -1416,8 +1435,9 @@ static unsigned get_codecs_sorted(const AVCodecDescriptor ***rcodecs)
return 0;
}
desc = NULL;
while ((desc = avcodec_descriptor_next(desc)))
while ((desc = avcodec_descriptor_next(desc))) {
codecs[i++] = desc;
}
switch_assert(i == nb_codecs);
qsort(codecs, nb_codecs, sizeof(*codecs), compare_codec_desc);
*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");
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, ")");
}

View File

@ -282,8 +282,15 @@ static switch_status_t add_stream(MediaStream *mst, AVFormatContext *fc, AVCodec
int buffer_bytes = 2097152; /* 2 mb */
int fps = 15;
/* find the encoder */
*codec = avcodec_find_encoder(codec_id);
if (mm->try_hardware_encoder && codec_id == AV_CODEC_ID_H264) {
*codec = avcodec_find_encoder_by_name("nvenc_h264");
}
if (!*codec) {
/* find the encoder */
*codec = avcodec_find_encoder(codec_id);
}
if (!(*codec)) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not find encoder for '%s'\n", avcodec_get_name(codec_id));
return status;
@ -360,13 +367,15 @@ static switch_status_t add_stream(MediaStream *mst, AVFormatContext *fc, AVCodec
c->ticks_per_frame = 2;
c->coder_type = 1; // coder = 1
c->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop
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->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) {
case SWITCH_VIDEO_PROFILE_BASELINE:
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->keyint_min = 25; // keyint_min=25
c->scenechange_threshold = 40; // sc_threshold=40
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->qmin = 10; // qmin=10
c->qmax = 31; // qmax=31
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) {
@ -493,8 +500,18 @@ static switch_status_t open_audio(AVFormatContext *fc, AVCodec *codec, MediaStre
c = mst->st->codec;
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) {
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;
}
@ -677,21 +694,21 @@ static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void *
eh->video_st->frame->pts += delta;
} else {
switch_core_timer_sync(eh->timer);
if (eh->video_st->frame->pts == eh->timer->samplecount) {
// never use the same pts, or the encoder coughs
eh->video_st->frame->pts++;
} else {
uint64_t delta_tmp = eh->timer->samplecount - last_ts;
if (delta_tmp > 10) {
delta = delta_tmp;
}
eh->video_st->frame->pts = eh->timer->samplecount;
}
}
last_ts = eh->video_st->frame->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);
ret = write_frame(eh->fc, &eh->video_st->st->codec->time_base, eh->video_st->st, &pkt);
switch_mutex_unlock(eh->mutex);
av_free_packet(&pkt);
av_packet_unref(&pkt);
}
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);
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) {
break;
@ -733,8 +750,10 @@ static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void *
switch_mutex_lock(eh->mutex);
ret = write_frame(eh->fc, &eh->video_st->st->codec->time_base, eh->video_st->st, &pkt);
switch_mutex_unlock(eh->mutex);
av_free_packet(&pkt);
av_packet_unref(&pkt);
if (ret < 0) break;
} else {
break;
}
}
@ -1075,7 +1094,7 @@ SWITCH_STANDARD_APP(record_av_function)
if (got_packet) {
ret = write_frame(fc, &video_st.st->codec->time_base, video_st.st, &pkt);
av_free_packet(&pkt);
av_packet_unref(&pkt);
goto again;
}
}
@ -1245,6 +1264,8 @@ struct av_file_context {
switch_image_t *last_img;
int read_fps;
switch_time_t last_vid_push;
int64_t seek_ts;
switch_bool_t read_paused;
};
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);
}
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.). */
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));
@ -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];
if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) {
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));
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_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_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_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_channel_layout", handle->channels == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO, 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);
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) {
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) {
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)) {
switch_yield(context->has_video ? 1000 : 10000);
continue;
@ -1434,13 +1484,13 @@ again:
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));
av_free_packet(&pkt);
av_packet_unref(&pkt);
av_frame_free(&vframe);
break;
}
// 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) {
// 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);
if (img) {
uint64_t *pts = malloc(sizeof(uint64_t));
int64_t *pts = malloc(sizeof(int64_t));
if (pts) {
#ifdef ALT_WAY
@ -1537,12 +1587,12 @@ again:
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));
av_free_packet(&pkt);
av_packet_unref(&pkt);
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);
av_free_packet(&pkt);
av_packet_unref(&pkt);
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);
@ -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));
handle->private_info = context;
context->pool = handle->memory_pool;
context->seek_ts = -1;
context->offset = DFT_RECORD_OFFSET;
if (handle->params && (tmp = switch_event_get_header(handle->params, "av_video_offset"))) {
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;
if ((handle->stream_name && (!strcasecmp(handle->stream_name, "rtmp") || !strcasecmp(handle->stream_name, "youtube")))) {
if (fmt->video_codec != AV_CODEC_ID_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_mutex_unlock(context->mutex);
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:
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)
{
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;
}
@ -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;
double fl_to = 0.02;
int do_fl = 0;
int smaller_ts = context->read_fps;
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;
}
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);
if (flags & SVR_FLUSH) {
max_delta = fl_to;
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 (mst->next_pts && (switch_time_now() - mst->next_pts > max_delta)) {
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) {
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;
@ -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);
// 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) {
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 ((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);
max_delta = AV_TIME_BASE;
//max_delta = AV_TIME_BASE;
if (switch_queue_size(context->eh.video_queue) > 0) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "WTF again\n");
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;
context->video_start_time = 0;
return SWITCH_STATUS_BREAK;
}
}
}
if ((flags & SVR_BLOCK) || do_fl) {

View File

@ -2,7 +2,7 @@ include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_avmd
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_LIBADD = $(switch_builddir)/libfreeswitch.la
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 "amplitude.h"
#include "psi.h"
#include "avmd_amplitude.h"
#include "avmd_psi.h"
/*! \brief
* @author Eric des Courtis
@ -10,14 +12,13 @@
* @param f Frequency estimate
* @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;
result = sqrt(PSI(b, i) / sin(f * f));
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__
#include "buffer.h"
#include "avmd_buffer.h"
#endif
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 <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; \
} 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) \
{ \
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)->i + (b)->pos), \
( \
((f)[(b)->i] >= 0) ? \
((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MAX): \
(0.0 - ((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MIN)) \
(BUFF_TYPE)(f)[(b)->i] \
) \
); \
} \
@ -92,14 +104,14 @@ extern size_t next_power_of_2(size_t v);
//#define DESTROY_CIRC_BUFFER(b) free((b)->buf)
#define GET_BACKLOG_POS(b) ((b)->lpos - (b)->backlog)
#define GET_CURRENT_POS(b) ((b)->lpos)
#define GET_CURRENT_SAMPLE(b) GET_SAMPLE((b), GET_CURRENT_POS((b)))
#define GET_CURRENT_POS(b) ((b)->pos)
#define GET_CURRENT_LPOS(b) ((b)->lpos)
#define GET_CURRENT_SAMPLE(b) GET_SAMPLE((b), GET_CURRENT_LPOS((b)))
#define ADD_SAMPLE(b, s) \
do { \
INC_POS((b)); \
SET_SAMPLE((b), GET_CURRENT_POS((b)), (s)); \
SET_SAMPLE((b), GET_CURRENT_LPOS((b)), (s)); \
} while (0)
#endif
#endif /* __AVMD_BUFFER_H__ */

View File

@ -1,4 +1,6 @@
#ifndef __DESA2_H__
#ifndef __AVMD_DESA2_H__
#include <stdio.h>
#ifdef WIN32
#include <float.h>
@ -6,15 +8,15 @@
#else
#define ISNAN(x) (isnan(x))
#endif
#include "buffer.h"
#include "desa2.h"
#include "options.h"
#include "avmd_buffer.h"
#include "avmd_desa2.h"
#include "avmd_options.h"
#ifdef FASTMATH
#include "fast_acosf.h"
#ifdef AVMD_FAST_MATH
#include "avmd_fast_acosf.h"
#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 n;
@ -33,14 +35,11 @@ extern double desa2(circ_buffer_t *b, size_t i)
x4 = GET_SAMPLE((b), ((i) + 4));
x2sq = x2 * x2;
d = 2.0 * ((x2sq) - (x1 * x3));
if (d == 0.0) return 0.0;
n = ((x2sq) - (x0 * x4)) - ((x1 * x1) - (x0 * x2)) - ((x3 * x3) - (x2 * x4));
#ifdef FASTMATH
#ifdef AVMD_FAST_MATH
result = 0.5 * (double)fast_acosf((float)n/d);
#else
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__
#define __PSI_H__
#include "buffer.h"
#ifndef __AVMD_PSI_H__
#define __AVMD_PSI_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)))
#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 <stdlib.h>
#ifndef _MSC_VER
@ -7,7 +17,7 @@
#endif
#include <string.h>
#include <assert.h>
#include "buffer.h"
#include "avmd_buffer.h"
typedef struct {
size_t len;
@ -31,7 +41,8 @@ typedef struct {
#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 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) \
{ \
@ -41,16 +52,19 @@ typedef struct {
#define APPEND_SMA_VAL(b, v) \
{ \
INC_SMA_POS(b); \
(b)->sma -= ((b)->data[(b)->pos] / (BUFF_TYPE)(b)->len); \
(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) \
{ \
(b)->sma = 0.0; \
(void)memset((b)->data, 0, sizeof(BUFF_TYPE) * (b)->len); \
(b)->pos = 0; \
(b)->lpos = 0; \
}
/*
@ -60,7 +74,11 @@ typedef struct {
}while(0);
*/
#endif
#endif /* __AVMD_SMA_BUFFER_H__ */
/*
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>
</ItemGroup>
<ItemGroup>
<ClInclude Include="amplitude.h">
<ClInclude Include="avmd_amplitude.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="buffer.h">
<ClInclude Include="avmd_buffer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="desa2.h">
<ClInclude Include="avmd_desa2.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fast_acosf.h">
<ClInclude Include="avmd_fast_acosf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="goertzel.h">
<ClInclude Include="avmd_goertzel.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="options.h">
<ClInclude Include="avmd_options.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="psi.h">
<ClInclude Include="avmd_psi.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="sma_buf.h">
<ClInclude Include="avmd_sma_buf.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="amplitude.c" />
<ClCompile Include="buffer.c" />
<ClCompile Include="desa2.c" />
<ClCompile Include="fast_acosf.c" />
<ClCompile Include="goertzel.c" />
<ClCompile Include="avmd_amplitude.c" />
<ClCompile Include="avmd_buffer.c" />
<ClCompile Include="avmd_desa2.c" />
<ClCompile Include="avmd_fast_acosf.c" />
<ClCompile Include="avmd_goertzel.c" />
<ClCompile Include="mod_avmd.c" />
</ItemGroup>
</Project>

View File

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

View File

@ -25,9 +25,14 @@
*
* 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 <g711.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -39,12 +44,23 @@
#define ISNAN(x) (isnan(x))
#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 */
#define SAMPLES_PER_MS(r, m) ((r) / (1000/(m)))
/*! Minimum beep length */
#define BEEP_TIME (100)
#define BEEP_TIME (2)
/*! 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 */
#define SINE_LEN(r) SAMPLES_PER_MS((r), SINE_TIME)
/*! How long in samples is the minimum beep length */
@ -59,23 +75,27 @@
#define TO_HZ(r, f) (((r) * (f)) / (2.0 * M_PI))
/*! Minimum beep frequency in Hertz */
#define MIN_FREQUENCY (300.0)
/*! Minimum frequency as digital normalized frequency */
#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)
/*! Maximum frequency as digital normalized frequency */
#define MAX_FREQUENCY_R(r) ((2.0 * M_PI * MAX_FREQUENCY) / (r))
/* decrease this value to eliminate false positives */
#define VARIANCE_THRESHOLD (0.001)
#define VARIANCE_THRESHOLD (0.00025)
#include "amplitude.h"
#include "buffer.h"
#include "desa2.h"
//#include "goertzel.h"
#include "psi.h"
#include "sma_buf.h"
#include "options.h"
#ifdef FASTMATH
#include "fast_acosf.h"
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
/* increase this value to eliminate false positives */
#define SAMPLES_CONSECUTIVE_STREAK 15
#endif
/*! Syntax of the API call. */
@ -87,13 +107,16 @@
/*! FreeSWITCH CUSTOM event type. */
#define AVMD_EVENT_BEEP "avmd::beep"
#define AVMD_CHAR_BUF_LEN 10
#define AVMD_BUF_LINEAR_LEN 160
/* Prototypes */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown);
SWITCH_STANDARD_API(avmd_api_main);
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);
/*! Status of the beep detection */
@ -121,11 +144,18 @@ typedef struct {
/* freq_table_t ft; */
avmd_state_t state;
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;
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 void init_avmd_session_data(avmd_session_t *avmd_session, switch_core_session_t *fs_session);
static switch_bool_t avmd_callback(switch_media_bug_t * bug,
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.
@ -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 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 */
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->pos = 0;
avmd_session->f = 0.0;
avmd_session->state.last_beep = 0;
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(
&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.
* @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;
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);
avmd_session->rate = read_codec->implementation->samples_per_second;
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;
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;
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:
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.
*
* @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)
{
#ifdef AVMD_FAST_MATH
char err[150];
int ret;
#endif
switch_application_interface_t *app_interface;
switch_api_interface_t *api_interface;
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
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;
}
switch_log_printf(
SWITCH_CHANNEL_LOG,
SWITCH_LOG_NOTICE,
"Advanced Voicemail detection enabled\n"
);
);
#ifdef FASTMATH
init_fast_acosf();
#ifdef AVMD_FAST_MATH
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_CHANNEL_LOG,
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
@ -246,9 +345,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load)
avmd_start_function,
"[start] [stop]",
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 */
return SWITCH_STATUS_SUCCESS;
@ -266,6 +366,7 @@ SWITCH_STANDARD_APP(avmd_start_function)
switch_status_t status;
switch_channel_t *channel;
avmd_session_t *avmd_session;
switch_media_bug_flag_t flags = 0;
if (session == NULL)
return;
@ -293,10 +394,19 @@ SWITCH_STANDARD_APP(avmd_start_function)
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);
#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(
session,
"avmd",
@ -304,7 +414,7 @@ SWITCH_STANDARD_APP(avmd_start_function)
avmd_callback,
avmd_session,
0,
SMBF_READ_REPLACE,
flags,
&bug
);
@ -328,11 +438,34 @@ SWITCH_STANDARD_APP(avmd_start_function)
*/
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown)
{
#ifdef AVMD_FAST_MATH
int res;
#endif
switch_event_free_subclass(AVMD_EVENT_BEEP);
#ifdef FASTMATH
destroy_fast_acosf();
#ifdef AVMD_FAST_MATH
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
switch_log_printf(
@ -363,6 +496,7 @@ SWITCH_STANDARD_API(avmd_api_main)
char *ccmd = NULL;
char *uuid;
char *command;
switch_core_media_flag_t flags = 0;
/* No command? Display usage */
if (zstr(cmd)) {
@ -429,10 +563,19 @@ SWITCH_STANDARD_API(avmd_api_main)
/* Allocate memory attached to this FreeSWITCH session for
* 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);
#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
* reading leg of the audio stream */
status = switch_core_media_bug_add(
@ -442,7 +585,7 @@ SWITCH_STANDARD_API(avmd_api_main)
avmd_callback,
avmd_session,
0,
SMBF_READ_REPLACE,
flags,
&bug
);
@ -478,6 +621,7 @@ end:
/*! \brief Process one frame of data with avmd algorithm.
* @author Eric des Courtis
* @par Modifications: Piotr Gregor
* @param session An avmd session.
* @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;
size_t pos;
double f;
double omega;
#ifdef AVMD_DEBUG
double f;
#endif
double v;
// double error = 0.0;
// double success = 0.0;
// double amp = 0.0;
// double s_rate;
// double e_rate;
// double avg_a;
//double sine_len;
double sma_digital_freq;
uint32_t sine_len_i;
//uint32_t beep_len_i;
// int valid;
char buf[AVMD_CHAR_BUF_LEN];
int sample_to_skip_n = AVMD_SAMLPE_TO_SKIP_N;
size_t sample_n = 0;
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;
/*! 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 = (double)sine_len_i;
//beep_len_i = BEEP_LEN(session->rate);
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);
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 */
for (pos = session->pos; pos < (GET_CURRENT_POS(b) - P); pos++) {
if ((pos % sine_len_i) == 0) {
/*! Get a desa2 frequency estimate every sine len */
f = desa2(b, pos);
if (f < MIN_FREQUENCY_R(session->rate) || f > MAX_FREQUENCY_R(session->rate)) {
if (omega < -0.999999 || omega > 0.999999) {
#ifdef AVMD_DEBUG
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session),
SWITCH_LOG_DEBUG, "<<< AVMD RESET >>>\n");
#endif
v = 99999.0;
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
RESET_SMA_BUFFER(&session->sma_b);
RESET_SMA_BUFFER(&session->sqa_b);
session->samples_streak = SAMPLES_CONSECUTIVE_STREAK;
sample_to_skip_n = AVMD_SAMLPE_TO_SKIP_N;
#endif
} else {
APPEND_SMA_VAL(&session->sma_b, f);
APPEND_SMA_VAL(&session->sqa_b, f * f);
if (isnan(omega)) {
#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);
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_DEBUG
#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 */
if (v < VARIANCE_THRESHOLD) {
switch_channel_set_variable_printf(channel, "avmd_total_time", "%d", (int)(switch_micro_time_now() - session->start_time) / 1000);
/* DECISION */
/* If variance is less than threshold
* and we have at least two estimates
* 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");
/*! Throw an event to FreeSWITCH */
/* Throw an event to FreeSWITCH */
status = switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, AVMD_EVENT_BEEP);
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, "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, "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;
switch_core_session_queue_event(session->session, &event);
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");
RESET_SMA_BUFFER(&session->sma_b);
RESET_SMA_BUFFER(&session->sqa_b);
session->state.beep_state = BEEP_DETECTED;
return;
}
//amp = 0.0;
//success = 0.0;
//error = 0.0;
goto done;
}
}
loop_continue:
++sample_n;
}
session->pos = pos;
done:
session->pos += sample_n;
session->pos &= b->mask;
return;
}
/* 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]"},
{"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>]"},
{"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>]"},
{"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>"},
{"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>"},
{"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]>"},
{"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>]"},
@ -135,6 +137,34 @@ switch_status_t conference_api_sub_pause_play(conference_obj_t *conference, swit
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 */
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) {
return SWITCH_STATUS_SUCCESS;
}
layer = conference_video_get_layer_locked(member);
if (layer) {
conference_video_clear_layer(layer);
if ((layer = conference_video_get_layer_locked(member))) {
layer->clear = 1;
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_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)
{
video_layout_t *vlayout = NULL;
char *group_name = NULL;
int idx = 0;
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)) {
layout_group_t *lg = NULL;
char *group_name = NULL;
int xx = 4;
if ((group_name = strchr(argv[2], ':'))) {
@ -1197,7 +1256,7 @@ switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, swit
} else {
group_name = argv[3];
}
if (!group_name) {
stream->write_function(stream, "Group name not specified.\n");
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_utils_set_flag(conference, CFLAG_REFRESH_LAYOUT);
return SWITCH_STATUS_SUCCESS;
} else {
vlayout = conference_video_find_best_layout(conference, lg, 0);
}
}
if (!vlayout) {
stream->write_function(stream, "Invalid group layout [%s]\n", group_name);
return SWITCH_STATUS_SUCCESS;
} else {
group_name = NULL;
}
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]) {
idx = atoi(argv[xx]);
if ((idx = atoi(argv[xx])) > 0) {
idx--;
}
}
}
}
if (!vlayout && (vlayout = switch_core_hash_find(conference->layout_hash, argv[2]))) {
conference->video_layout_group = NULL;
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]);
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;
switch_mutex_unlock(conference->member_mutex);
} else {
stream->write_function(stream, "Change canvas %d to layout [%s]\n", idx + 1, vlayout->name);
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);
}
@ -1589,6 +1651,24 @@ switch_status_t conference_api_sub_get_uuid(conference_member_t *member, switch_
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)
{
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))) {
member->video_reservation_id = NULL;
stream->write_function(stream, "+OK reservation_id cleared\n");
conference_video_detach_video_layer(member);
} 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);
conference_video_detach_video_layer(member);
}
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);
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);
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))) {
uint32_t id = atoi(var) - 1;
if (id == 0) {
id = conference->canvas_count;
}
if (id <= conference->canvas_count && conference->canvases[id]) {
member->watching_canvas_id = id;
}
}
conference_video_reset_member_codec_index(member);
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);
}
}
} 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;
} else if (!strcasecmp(argv[i], "join-only")) {
f[MFLAG_JOIN_ONLY] = 1;
} else if (!strcasecmp(argv[i], "flip-video")) {
f[MFLAG_FLIP_VIDEO] = 1;
} else if (!strcasecmp(argv[i], "positional")) {
f[MFLAG_POSITIONAL] = 1;
} 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)
{
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)
{
switch_mutex_lock(member->flag_mutex);
member->flags[flag] = 1;
conference_utils_member_set_flag(member, flag);
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;
}
if (layer->clear) {
conference_video_clear_layer(layer);
layer->clear = 0;
}
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);
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_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))) {
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) {
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;
if (!member || member->canvas_id < 0 || member->video_layer_id < 0) return NULL;
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) {
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;
switch_frame_t write_frame = { 0 }, *frame = NULL;
switch_status_t encode_status = SWITCH_STATUS_FALSE;
switch_image_t *scaled_img = codec_set->scaled_img;
write_frame = codec_set->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);
}
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 {
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;
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) {
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) {
mcu_layer_t *layer = NULL;
mcu_canvas_t *canvas = NULL;
canvas = conference_video_get_canvas_locked(member);
switch_mutex_lock(canvas->mutex);
layer = &canvas->layers[member->video_layer_id];
switch_img_free(&layer->mute_img);
switch_img_free(&member->video_mute_img);
if ((canvas = conference_video_get_canvas_locked(member))) {
switch_mutex_lock(canvas->mutex);
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) {
switch_img_copy(layer->cur_img, &member->video_mute_img);
switch_img_copy(layer->cur_img, &layer->mute_img);
if (!clear && layer->cur_img) {
switch_img_copy(layer->cur_img, &member->video_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];
layer = &canvas->layers[member->video_layer_id];
if (!layer->need_patch || switch_thread_rwlock_tryrdlock(canvas->video_rwlock) != SWITCH_STATUS_SUCCESS) {
canvas = NULL;
layer = NULL;
if (layer->need_patch && switch_thread_rwlock_tryrdlock(canvas->video_rwlock) == SWITCH_STATUS_SUCCESS) {
if (layer->need_patch) {
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);
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;
}
if (conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) {
return;
}
canvas = conference_video_get_canvas_locked(member);
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;
canvas->video_timer_reset = 1;
canvas->video_layout_group = conference->video_layout_group;
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_frame_t file_frame = { 0 };
int j = 0, personal = conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS) ? 1 : 0;
int video_count = 0;
if (!personal) {
switch_mutex_lock(canvas->mutex);
if (canvas->new_vlayout) {
if (canvas->new_vlayout && switch_mutex_trylock(conference->canvas_mutex) == SWITCH_STATUS_SUCCESS) {
conference_video_init_canvas_layers(conference, canvas, NULL);
switch_mutex_unlock(conference->canvas_mutex);
}
switch_mutex_unlock(canvas->mutex);
}
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;
}
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);
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) {
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) {
layout_group_t *lg = 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) &&
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) {
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))) {
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, canvas->video_count))) {
switch_mutex_lock(conference->member_mutex);
canvas->new_vlayout = vlayout;
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.packetlen = buflen;
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);
}
@ -2348,7 +2407,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
}
//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) {
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);
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) {
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;
}
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) {
conference_video_init_canvas_layers(conference, imember->canvas, conference->new_personal_vlayout);
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)) {
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;
}
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))) {
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;
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_read_lock(imember->session) != SWITCH_STATUS_SUCCESS)) {
continue;
@ -2669,6 +2725,10 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
use_img = omember->pcanvas_img;
if (files_playing && layer && layer == &imember->canvas->layers[imember->canvas->layout_floor_id]) {
use_img = NULL;
}
if (layer) {
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++) {
if (write_codecs[i] && switch_core_codec_ready(&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);
}
if (conference->canvas_count > 1) {
return;
}
if (member && member->video_reservation_id) {
/* no video floor when a reservation id is set */
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 &&
!member->conference->playing_video_file) {
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) {
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++;
}
@ -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)
{
if (fnode) {
switch_core_file_command(&fnode->fh, SCFC_PAUSE_READ);
if (switch_test_flag(fnode, NFLAG_PAUSE)) {
stream->write_function(stream, "+OK Resume\n");
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)
{
@ -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;
uint32_t force_rate_i = 0, force_interval_i = 0, force_channels_i = 0, video_auto_floor_msec = 0;
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 */
if (zstr(name)) {
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 {
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->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)) {
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");
@ -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)) {
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;

View File

@ -208,6 +208,7 @@ typedef enum {
MFLAG_CAN_BE_SEEN,
MFLAG_SECOND_SCREEN,
MFLAG_SILENT,
MFLAG_FLIP_VIDEO,
///////////////////////////
MFLAG_MAX
} member_flag_t;
@ -439,6 +440,7 @@ typedef struct mcu_layer_s {
int mute_patched;
int avatar_patched;
int refresh;
int clear;
int is_avatar;
switch_size_t last_img_addr;
switch_img_position_t logo_pos;
@ -484,6 +486,8 @@ typedef struct mcu_canvas_s {
int refresh;
int send_keyframe;
int play_file;
int video_count;
char *video_layout_group;
switch_rgb_color_t bgcolor;
switch_rgb_color_t border_color;
switch_rgb_color_t letterbox_bgcolor;
@ -662,6 +666,12 @@ typedef struct conference_obj {
video_layout_t *new_personal_vlayout;
int max_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;
/* Relationship with another member */
@ -793,6 +803,9 @@ typedef struct codec_set_s {
switch_codec_t codec;
switch_frame_t frame;
uint8_t *packet;
switch_image_t *scaled_img;
uint8_t fps_divisor;
uint32_t frame_count;
} codec_set_t;
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_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 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_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_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_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);
@ -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_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_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_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);

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)
{
route_callback_t *cbt = (route_callback_t *) pArg;
switch_copy_string(cbt->gateway, argv[0], 128);
switch_copy_string(cbt->group, argv[1], 128);
switch_copy_string(cbt->limit, argv[2], 128);
switch_copy_string(cbt->techprofile, argv[3], 128);
switch_copy_string(cbt->acctcode, argv[4], 128);
switch_copy_string(cbt->translated, argv[5], 60);
if (argc >= 6) {
switch_copy_string(cbt->gateway, argv[0], 128);
switch_copy_string(cbt->group, argv[1], 128);
switch_copy_string(cbt->limit, argv[2], 128);
switch_copy_string(cbt->techprofile, argv[3], 128);
switch_copy_string(cbt->acctcode, argv[4], 128);
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;
}

View File

@ -367,7 +367,8 @@ SWITCH_STANDARD_APP(play_fsv_function)
goto end;
}
switch_core_session_set_read_codec(session, &codec);
switch_channel_set_flag(channel, CF_VIDEO_WRITING);
while (switch_channel_ready(channel)) {
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_session_set_read_codec(session, NULL);
switch_channel_clear_flag(channel, CF_VIDEO_WRITING);
if (switch_core_codec_ready(&codec)) {
switch_core_codec_destroy(&codec);
@ -510,6 +510,8 @@ SWITCH_STANDARD_APP(play_yuv_function)
switch_channel_answer(channel);
switch_core_session_request_video_refresh(session);
switch_channel_audio_sync(channel);
switch_core_session_raw_read(session);
@ -529,7 +531,7 @@ SWITCH_STANDARD_APP(play_yuv_function)
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)) {
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];
// switch_channel_set_flag(channel, CF_VIDEO_PASSIVE);
switch_channel_set_flag(channel, CF_VIDEO_WRITING);
//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_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;
} 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_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;
}
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) */
SWITCH_MODULE_LOAD_FUNCTION(mod_memcache_load)
{
switch_api_interface_t *api_interface;
switch_file_interface_t *file_interface;
/* connect my internal structure to the blank pointer passed to me */
*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");
/* 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 */
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)))) {
return SWITCH_STATUS_FALSE;
}
memset(sess, 0, sizeof(*sess));
sess->encoder.lastrxseqno = -1;
sess->decoder.lastrxseqno = -1;
sess->pool = codec->memory_pool;
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);
switch_mutex_lock(g_sessions_lock);
if (encoding) {
sess->encoder.request.usr_priv = sess;
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++;
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_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_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;
return SWITCH_STATUS_SUCCESS;
@ -864,14 +864,13 @@ static switch_status_t switch_sangoma_destroy(switch_codec_t *codec)
if (sess->encoder.txrtp) {
sngtc_free_transcoding_session(&sess->encoder.reply);
memset(&sess->encoder, 0, sizeof(sess->encoder));
}
if (sess->decoder.txrtp) {
sngtc_free_transcoding_session(&sess->decoder.reply);
memset(&sess->decoder, 0, sizeof(sess->decoder));
}
switch_core_hash_delete(g_sessions_hash, sess->hashkey);
memset(sess, 0, sizeof(*sess));
switch_mutex_unlock(g_sessions_lock);
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];
#ifndef _WIN32
#else
snprintf(buf, sizeof(buf), "/tmp/rtmp-%s-in.txt", (*newsession)->uuid);
(*newsession)->io_debug_in = fopen(buf, "w");
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,
where we should send media */
switch_size_t dropped_video_frame;
switch_queue_t *video_send_queue;
uint8_t media_debug;
};

View File

@ -41,6 +41,17 @@ typedef struct {
size_t len;
} 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)
{
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);
}
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 */
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;
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",
// 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)) {
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;
}
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "=== send type: %d ts: %d bytes: %zu\n", type, timestamp, len);
state->ts = timestamp;
state->type = type;
state->origlen = len;
@ -740,6 +761,79 @@ end:
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 */
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)) {
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)) {
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);
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;
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);
}
}
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:
switch_buffer_zero(helper->rtmp_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->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(
"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->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(
"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->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(
"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->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(
"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]) {
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;
}
@ -199,11 +202,17 @@ error:
}
listener->profile->ib_failed_calls++;
if ( button ) {
switch_safe_free(button);
}
return SWITCH_STATUS_FALSE;
done:
*session = nsession;
listener->profile->ib_calls++;
if ( button ) {
switch_safe_free(button);
}
return SWITCH_STATUS_SUCCESS;
}
@ -1524,6 +1533,7 @@ switch_status_t skinny_handle_stimulus_message(listener_t *listener, skinny_mess
return SWITCH_STATUS_FALSE;
}
skinny_session_process_dest(session, listener, line_instance, button_speed_dial->line, '\0', 0);
switch_safe_free(button_speed_dial);
}
break;
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);
if ( ! session ) {
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;
}
skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 0);
}
switch_safe_free(button_line);
break;
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);
switch_safe_free(button);
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));
switch_safe_free(button);
skinny_send_reply(listener, message, SWITCH_TRUE);
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);
switch_safe_free(button);
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);
switch_safe_free(button);
return SWITCH_STATUS_SUCCESS;
}
@ -2568,7 +2588,7 @@ switch_status_t skinny_handle_updatecapabilities(listener_t *listener, skinny_me
}
i = 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++) {
char *codec = codec_order[i];
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,
"Codecs %s supported.\n", codec_string);
switch_safe_free(codec_string);
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->proxy_refer_msg) {
msg_ref_destroy(tech_pvt->proxy_refer_msg);
tech_pvt->proxy_refer_msg = NULL;
}
if (tech_pvt->respond_phrase) {
switch_yield(100000);
}
@ -598,6 +603,15 @@ switch_status_t sofia_on_hangup(switch_core_session_t *session)
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);
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) {
#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:
{
if (!switch_channel_test_flag(channel, CF_AVPF)) {
//const char *ua = switch_channel_get_variable(tech_pvt->channel, "sip_user_agent");
//if (ua && switch_stristr("polycom", ua)) {
@ -1341,7 +1392,6 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
//}
}
break;
#endif
case SWITCH_MESSAGE_INDICATE_BROADCAST:
{
const char *ip = NULL, *port = NULL;
@ -1945,34 +1995,6 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
}
}
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:
{
@ -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)) {
char *p;
@ -5008,7 +5040,7 @@ static int notify_callback(void *pArg, int argc, char **argv, char **columnNames
return 0;
}
static void general_event_handler(switch_event_t *event)
void general_event_handler(switch_event_t *event)
{
switch (event->event_id) {
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)
{
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);
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.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;
}
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");
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");
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");
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");
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");
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(general_event_handler);
switch_event_unbind_callback(general_queue_event_handler);
switch_event_unbind_callback(event_handler);
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_PROFILE_START "sofia::profile_start"
#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_TRANSFEREE "sofia::transferee"
@ -365,6 +366,7 @@ struct mod_sofia_globals {
char hostname[512];
switch_queue_t *presence_queue;
switch_queue_t *msg_queue;
switch_queue_t *general_event_queue;
switch_thread_t *msg_queue_thread[SOFIA_MAX_MSG_QUEUE];
int msg_queue_len;
struct sofia_private destroy_private;
@ -800,6 +802,8 @@ struct private_object {
char *x_freeswitch_support_local;
char *last_sent_callee_id_name;
char *last_sent_callee_id_number;
char *proxy_refer_uuid;
msg_t *proxy_refer_msg;
switch_mutex_t *flag_mutex;
switch_mutex_t *sofia_mutex;
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_queue_message(sofia_dispatch_event_t *de);
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,
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,
sofia_dispatch_event_t *de, tagi_t tags[]);
void sofia_handle_sip_r_notify(switch_core_session_t *session, int status,
char const *phrase,
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[])
{
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)) {
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,
char const *phrase,
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 &&
sip->sip_content_type && sip->sip_content_type->c_type &&
switch_stristr("sipfrag", sip->sip_content_type->c_type)) {
switch_core_session_t *other_session;
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
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, sip->sip_payload->pl_data);
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), TAG_END());
goto end;
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 && switch_stristr("sipfrag", sip->sip_content_type->c_type)) {
if (sofia_pass_notify(session, tech_pvt->proxy_refer_uuid, sip->sip_payload->pl_data) == SWITCH_STATUS_SUCCESS) {
if (tech_pvt->proxy_refer_msg) {
msg_ref_destroy(tech_pvt->proxy_refer_msg);
tech_pvt->proxy_refer_msg = NULL;
}
tech_pvt->proxy_refer_msg = msg_ref_create(de->data->e_msg);
//nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
} else {
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
goto end;
}
/* 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
static void our_sofia_event_callback(nua_event_t event,
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);
break;
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;
case nua_i_notify:
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;
case nua_r_refer:
if (session) {
sofia_handle_sip_r_refer(nua, profile, nh, session, status, phrase, sip, de, tags);
}
break;
case nua_i_refer:
if (session) {
@ -1958,18 +2027,14 @@ void sofia_process_dispatch_event_in_thread(sofia_dispatch_event_t **dep)
sofia_dispatch_event_t *de = *dep;
switch_memory_pool_t *pool;
//sofia_profile_t *profile = (*dep)->profile;
switch_thread_data_t *td;
switch_core_new_memory_pool(&pool);
*dep = NULL;
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 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 */
void *pop;
int tick = 0, x = 0;
sofia_set_pflag_locked(profile, PFLAG_WORKER_RUNNING);
while ((mod_sofia_globals.running == 1 && sofia_test_pflag(profile, PFLAG_RUNNING))) {
if (profile->watchdog_enabled) {
uint32_t event_diff = 0, step_diff = 0, event_fail = 0, step_fail = 0;
if (tick) {
if (profile->watchdog_enabled) {
uint32_t event_diff = 0, step_diff = 0, event_fail = 0, step_fail = 0;
if (profile->step_timeout) {
step_diff = (uint32_t) ((switch_time_now() - profile->last_root_step) / 1000);
if (profile->step_timeout) {
step_diff = (uint32_t) ((switch_time_now() - profile->last_root_step) / 1000);
if (step_diff > profile->step_timeout) {
step_fail = 1;
if (step_diff > profile->step_timeout) {
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) {
event_fail = 1;
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 (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) {
time_t now = switch_epoch_time_now(NULL);
sofia_reg_check_ping_expire(profile, now, profile->iping_seconds);
iping_loops = 0;
if(++iping_loops >= (uint32_t)profile->iping_freq) {
time_t now = switch_epoch_time_now(NULL);
sofia_reg_check_ping_expire(profile, now, profile->iping_seconds);
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) {
sofia_reg_check_gateway(profile, switch_epoch_time_now(NULL));
sofia_sub_check_gateway(profile, switch_epoch_time_now(NULL));
gateway_loops = 0;
}
tick = 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);
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 {
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:
tech_pvt->recv_invites++;
tech_pvt->sent_last_invite = 0;
if (!sofia_test_flag(tech_pvt, TFLAG_SDP)) {
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);
@ -7113,6 +7203,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
goto done;
}
}
if (r_sdp && !sofia_test_flag(tech_pvt, TFLAG_SDP)) {
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;
}
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)
|| ((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));
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 &&
((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);
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);
@ -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");
status = SWITCH_STATUS_FALSE;
switch_core_session_rwunlock(other_session);
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
goto done;
}
}
//}
}
@ -7397,6 +7491,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
goto done;
}
}
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 1);
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);
if(sofia_test_flag(other_tech_pvt, TFLAG_REINVITED)) {
other_tech_pvt = switch_core_session_get_private(other_session);
if (sofia_test_flag(other_tech_pvt, TFLAG_REINVITED)) {
/* 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");
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 (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);
}
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh, SIP_200_OK,
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str),
SOATAG_REUSE_REJECTED(1),
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());
}
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) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(session));
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,
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);
}
if (sofia_test_pflag(profile, PFLAG_PROXY_REFER)) {
switch_core_session_t *other_session;
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
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_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());
if (full_ref_to && sofia_test_pflag(profile, PFLAG_PROXY_REFER)) {
if (sofia_process_proxy_refer(session, full_ref_to) == SWITCH_STATUS_SUCCESS) {
if (tech_pvt->proxy_refer_msg) {
msg_ref_destroy(tech_pvt->proxy_refer_msg);
tech_pvt->proxy_refer_msg = NULL;
}
tech_pvt->proxy_refer_msg = msg_ref_create(de->data->e_msg);
//nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_EXPIRES_STR("60"), TAG_END());
goto done;
}
}
from = sip->sip_from;
//to = sip->sip_to;
@ -9104,6 +9207,11 @@ void sofia_handle_sip_i_reinvite(switch_core_session_t *session,
if (session) {
channel = switch_core_session_get_channel(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)) {
@ -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 &&
(sofia_test_pflag(profile, PFLAG_AUTH_CALLS)
|| (!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++;
}
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);
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);
sofia_glue_fire_events(profile);
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);
sofia_glue_fire_events(profile);
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 ((path_val = sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_path))) {
char *path_stripped = sofia_glue_get_url_from_contact(path_val, SWITCH_TRUE);
su_free(nua_handle_home(nh), path_val);
path_val = path_stripped;
path_encoded_len = (int)(strlen(path_val) * 3) + 1;
char *path_stripped = NULL;
char *path_val_to_encode = NULL;
su_strlst_t *path_list = su_strlst_create(nua_handle_home(nh));
sip_path_t *next_path = sip->sip_path;
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_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) {
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(dup_mwi_account);
switch_safe_free(utmp);
switch_safe_free(path_val);
su_free(nua_handle_home(nh), path_val);
switch_safe_free(token_val);
if (auth_params) {

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