====== IceStream ====== Icestream est un logiciel de stream audio qui permet de mixer, recevoir ou envoyer des flux audio à travers un réseau local ou Internet. ==== IceStream v2.1-2 : cooking - installation et utilisation ==== === 1 - La mise à jour du logiciel === Les nouvelles fonctionnalités et changements : General * Les paramètres peuvent désormais être sauvegardé et chargé, ainsi qu'importé et exporté * Amélioration de la gestion des chemins Lecteur * Ajout d'un vu-Mètre rudimentaire * Ajout d'une trackbar non interactive * Fix du contrôle de volume * Fix du comportement du lecteur * Amélioration général de la lisibilité générale Client * Transition d'ices à Liquidsoap * Ajout du support pour le format mp3 * Ajout d'une supervision des logs et statut du client * Ajout d'une option de sauvegarde du stream en local * Ajout d'une option de duplication du stream en basse qualité Server * Mise à jour vers IceCast2 * Ajout d'une vue Web vers le serveur * Ajout d'une supervision des logs et statut du client === 2 - Installation How-To === == [RECOMMENDÉ] Par package : == L'installation la plus facile se fait à partir du package (.deb) situé [[https://sourceforge.net/projects/icestream/files/|ici]]. Double-clicker (//ou click-droit -> Installer logiciel//) le fichier installera Icestream, ainsi que ces dépendances. == [ALTERNATIVE] == Vous pouvez également installer le package via dpkg, puis apt pour les dépendances : dpkg -i ~/chemin/vers/icestream_x.x-x_x.deb apt -f install == À partir des sources :== Si vous souhaitez compiler directement IceStream, vous trouverez ce dont vous avez besoin [[https://sourceforge.net/p/icestream/code/ci/master/tree/|ici]]. N'oubliez pas d'installer les dépendances nécessaire : apt install debhelper-compat, libvorbis-dev, libxml2-dev, libxslt1-dev, qt4-qmake, g++, libqt4-dev, libqtwebkit4 === 3 - Comment utiliser Icestream === L'interface consiste de trois onglets.\\ - Les lecteurs, pouvant jouer la majorité des formats audio et vidéo, que ce soit en local, ou en réseau.\\ - Le client, permettant de transmettre le flux vers un serveur.\\ - Le serveur, si vous souhaitez héberger votre propre serveur.\\ Pour lire un fichier/flux, il vous suffira d'insérer de cliquer sur le bouton représentant un dossier et de sélectionner votre audio, ou bien directement insérer le chemin/URL dans la barre de texte. Vous pourrez ensuite cliquer le bouton Play. Pour envoyé votre flux vers un serveur, il vous suffira d'entrer les paramètres nécessaire à la connexion (adresse, port, mot de passe). Vous pouvez également modifier les paramètres audio à votre guise (//Il est conseillé d'avoir un taux d’échantillonnage supérieur à 44100//).\\ Il vous faudra ensuite, dans l'onglet "Connections" de qjackctl, lier les lecteurs (//appelés "MPlayer"//) au client "Liquidsoap". Cette interface vous permettra de gérer quels flux sont envoyés au serveur, choisissant ainsi ce que l'auditeur peut entendre. Pour héberger votre serveur, il vous suffira de rentrer les paramètres demandés.\\ Le mot de passe et port demandés sont bien ceux qu'il faudra transmettre au client.\\ Une fois le serveur démarré, une fenêtre s'ouvrira vers la page de gestion de votre serveur, vous permettant de contrôler sont statut. == [ FAQ ] == - Mon audio ne joue pas !\\ -> Assurez vous d'avoir démarré JACK, via qjackctl. - J'héberge le serveur IceCast moi-même, mais je n'arrive pas à m'y connecter !\\ -> Assurez vous d'avoir correctement insérer l'adresse et le port de votre serveur. Il est également possible qu'IceCast tourne en fond. Pour régler cela, vous pouvez faire : /etc/init.d/icecast2 stop ===4 - Détails=== [**AVERTISSEMENT**]\\ Les solutions présentées ci-dessous ne sont absolument pas les meilleures pratiques possible, mais les ébauches d'un débutant.\\ Une grande partie des ces méthodes pourraient être améliorées.\\ Veillez donc à rester critique, et à garder votre propre vision des choses. Merci.\\ \\ Cette section consiste à expliquer en plus de détails les changements effectué au code\\ Note : IceStream à été conçu avec Qt Creator et Qt Designer, compilé sous qt4.8.\\ ==Fichiers de configurations Client/Serveur== __Serveur__\\ Un guide des plus complet peut être trouvé [[https://people.xiph.org/~epirat/Icecast/Docs/config_file/|ici]].\\ __Client__\\ La documentation du fichier de config Liquidsoap peut être trouvé [[https://www.liquidsoap.info/doc-dev/settings.html|ici]].\\ Exemple : set("log.stdout", false) set("log.file", true) set("log.file.path", "/tmp/liquidsoap.log") set("log.level", 3) set("frame.audio.size", 2048) full = input.jack(id="liquidsoap") output.icecast(%vorbis(samplerate=96000, channels=1 , quality=0.5), host="monhost.com", port=XXXX, password="monpass", mount="monnomdestream", name="test", description="test stream", genre="Etcetera", full) __Creation d'un fichier de config Client/Server__ La création d'un fichier de config ce sont en utilisant [[https://doc.qt.io/archives/qt-4.8/qfile.html|QFile]].\\ La configuration d'IceCast devant être sous format XML, il est sage d'utiliser [[https://doc.qt.io/archives/qt-4.8/qtextstream.html|QXmlStreamWriter]], qui permet l'écriture de contenu XML très simplement.\\ La configuration de Liquidsoap devant être en Plain Text, [[https://doc.qt.io/archives/qt-4.8/qtextstream.html|QTextStream]] sera utiliser. void CreateLiquidConfig() { QFile file(clientFilePath); file.open(QFile::WriteOnly | QFile::Text); QTextStream stream(&file); stream.setCodec("ISO 8859-1"); stream << "set(\"log.stdout\", false)" << endl; ... file.close(); ---------------------------------------------------------------------- QXmlStreamWriter writer(&file); writer.setCodec("ISO 8859-1"); writer.setAutoFormatting(true); writer.writeStartDocument(); writer.writeStartElement("icecast"); writer.writeEndElement(); ... file.close(); == Mise à jour IceCast2 == IceCast(1) étant obsolète, il a fallu passer à IceCast2.\\ Au niveau code, il a simplement fallu remplacer un mot-clef. Le fichier de configuration est lui resté le même. void launchIceCast() { [...] serverStreamCommande = "icecast2"; [...] } ==La vue Web== Une vue Web (comme un explorateur internet) vers la page d'admin du serveur à été ajouté. Pour cela on utilise un objet [[https://doc.qt.io/archives/qt-4.8/qwebview.html|QWebView]], placé dans l'UI via Qt Designer.\\ À la confirmation du bon lancement du serveur, on ouvre la vue, en lui donnant l'adresse vers où pointé :\\ Ici un [[https://doc.qt.io/archives/qt-4.8/qtimer.html|timer]] est utilisé pour donner un peu de temps au serveur de s'initialiser correctement. Tenter d'afficher la vue trop rapidement est voué à l'échec. [ Après lancement ] QTimer::singleShot(1000, this, SLOT(slotOpenAdminView())); void slotOpenAdminView(){ ui->iceWebView->load(iceAdminUrl); ui->iceWebView->show(); } ==Supervision des logs et statut d'un QProcess== Le but était d'avoir un œil sur le statut d'IceCast et de LiquidSoap\\ Les fichiers de logs se trouvent dans /tmp/ (voir configuration server/client)\\ Un [[https://doc.qt.io/archives/qt-4.8/qfilesystemwatcher.html|QFileSystemWatcher]] est utilisé ici afin d'obtenir un signal à chaque écriture dans le fichier liquidsoap_watcher = new QFileSystemWatcher(this); liquidsoap_watcher->addPath("/tmp/liquidsoap.log"); connect(liquidsoap_watcher,SIGNAL(fileChanged(QString)),SLOT(slotLiquidlog(QString))); slotLiquidlog(QString filepath) { QString log = getLogs(filepath); if((log != lastLiquidlog) && !(log.contains(lastLiquidlog))){ ui->liquidsoap_logWindow->append(log); lastLiquidlog = log ; } } Avec une fonction permettant de parser les nouvelles lignes ajoutées getLogs(QString path){ QFile file(path); QString log; QString lastlog[3]; if (file.open(QIODevice::ReadOnly | QIODevice::Text)){ file.seek(file.size()-1); for (int i=0;(i<=1) && (file.pos() > 0);) { QString token = file.read(1); file.seek(file.pos()-2); if (token == "\n"){ i++; } } log = file.readAll(); file.close(); log.remove(0, 2); log.remove(log.size()-1, 1); // Fix pour une erreur dans la lecture } return log; } **Note : Cette méthode complète est loin d'être parfaite, et devrait être amélioré si vous étiez à vous //inspirer// des ces snippets.** Les applications étant lancées en tant que [[https://doc.qt.io/archives/qt-4.8/qprocess.html|QProcess]], le statut (ON/OFF) est obtenue grâce aux changements du [[https://doc.qt.io/archives/qt-4.8/qprocess.html#stateChanged|QProcess::ProcessState]] tel que : connect(streamProcess,SIGNAL(stateChanged(QProcess::ProcessState)),SLOT(slotstreamProcess_StateChanged(QProcess::ProcessState))); void slotstreamProcess_StateChanged(QProcess::ProcessState i) { if(i == 0){ ui->streamStatus->setIcon(QIcon(ledred)); } // OFF else { ui->streamStatus->setIcon(QIcon(ledgreen)); } // ON } ==Ajout d'un format audio (ici mp3)== La documentation sur les formats audio se trouve [[https://www.liquidsoap.info/doc-dev/encoding_formats.html|ici]].\\ La configuration ayant besoin d'être donné à Liquidsoap concernant le format diffère de format à format.\\ // Exemple avec ogg output_setting = "output.icecast(%vorbis(samplerate=" + ui->comboBox_SampleRate_ogg->currentText() + ", channels=" + ui->comboBox_Channels_ogg->currentText() + " , quality=" + ui->comboBox_Quality_ogg->currentText() + "),"; La ligne correspondante au format doit pouvoir être interchangeable si l'on veut pouvoir utiliser plusieurs formats. void audioFormatSettingsPicker(int i){ QString output_setting; switch (i) { case 0: // ogg output_setting = "output.icecast(%vorbis(samplerate=" + ui->comboBox_SampleRate_ogg->currentText() + ", channels=" + ui->comboBox_Channels_ogg->currentText() + " , quality=" + ui->comboBox_Quality_ogg->currentText() + "),"; break; case 1: // mp3 output_setting = "output.icecast(%mp3.vbr(samplerate=" + ui->comboBox_SampleRate_mp3->currentText() + ", quality=" + ui->comboBox_Quality_mp3->currentText() + ", " + ui->comboBox_StereoMode_mp3->currentText() + "),"; break; Il faut également que l'utilisateur puisse sélectionner ce format et ses paramètres. Dans le cas d'IceStream, les paramètres de chaque formats sont contenu dans leurs propres QFrame.\\ C'est frame sont ensuite cachées et affichées selon le format sélectionné. connect(ui->comboBox_Format, SIGNAL(currentIndexChanged(int)),SLOT(audioFormatFrameHandler(int))); void audioFormatFrameHandler(int i){ switch (i) { case 0: ui->audioFrame_mp3->hide(); ui->audioFrame_mp3->setEnabled(0); ui->audioFrame_ogg->setEnabled(1); ui->audioFrame_ogg->show(); break; case 1: ui->audioFrame_ogg->hide(); ui->audioFrame_ogg->setEnabled(0); ui->audioFrame_mp3->setEnabled(1); ui->audioFrame_mp3->show(); break; ==Ajout d'une option Liquidsoap== Liquidsoap est un client puissant, offrant plus que la possibilité d'envoyer un flux.\\ Lors de la création du fichier de configuration, on utilisera "checkState()" de [[https://doc.qt.io/archives/qt-4.8/qcheckbox.html|QCheckBox]] afin de savoir si l'utilisateur à choisi cette option. void createLiquidConfig() { QFile file(clientFilePath); // Ouverture du fichier en écriture et en texte. file.open(QFile::WriteOnly | QFile::Text); QTextStream stream(&file); [...] if(ui->optionStreamDump->checkState()){ stream << "output.file(%wav(stereo=true, channels=2, samplesize=16, header=false)," << endl; stream << "'dump/%d-%m-%Y-%Hh%M.wav'," << endl; stream << "fallible=true," << endl; stream << "reopen_on_metadata=false," << endl; stream << "on_stop=shutdown," << endl; stream << "dump)" << endl; } ==Utilisation de QProcess== Un [[https://doc.qt.io/archives/qt-4.8/qprocess.html|QProcess]] permet de lancer une application/commande, et de lui attacher un objet.\\ L'exemple ci-dessous correspond à une version simplifié au lancement du vu-mètre d'IceStream.\\ QProcess à besoin du nom de l'application/commande quand une [[https://doc.qt.io/archives/qt-4.8/qstring.html|QString]], ainsi qu'une [[https://doc.qt.io/archives/qt-4.8/qstringlist.html|QStringList]] contenant tout possible argument que désirez passer à votre application/commande. void startVuMeter(){ QStringList arguments; QString output = "MPlayer [%1]"; arguments << "-t" << "dpm" << output_one.arg(playaProcess->pid())); vuMeterProcess->start("meterbridge", arguments); } Note : Ici est également démontré l'utilisation de la fonction [[https://doc.qt.io/archives/qt-4.8/qstring.html#arg|.arg]] de QString, remplacant "%1", par la valeur donnée. ==Le stdin & stdout de MPlayer== __Exemple : La trackbar__\\ Elle consiste d'une simple [[https://doc.qt.io/archives/qt-4.8/qprogressbar.html|QProgressBar]], personnalisé pour mieux s'accorder au concept d'une trackbar.\\ Le snippet si dessous correspond à la stylesheet utilisé pour IceStream. La syntax est dans l'esprit du CSS. QProgressBar::chunk { background-color:rgb(57, 119, 163); margin-left: 1px; margin-right: 1px; border: 1px solid rgb(110, 95, 100); border-radius: 3px; } La position de la piste est obtenu en envoyant à MPlayer la commande "get_time_pos" toutes les secondes. poller = new QTimer(this); poller->start(1000); connect(poller, SIGNAL(timeout()), this, SLOT(slotpollCurrentTime())); La réponse d'MPlayer est alors "ANS_TIME_POSITION=XX.x". Il suffit donc d'extraire cette valeur et de mettre à jour la barre. connect(lecteurProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(slotprocessMessage())); void slotprocessMessage(){ while (lecteurProcess->canReadLine()) { buffer.remove(0, 18); // Retire "ANS_TIME_POSITION=" currentTime = buffer.toFloat(); trackBar->setValue(static_cast(currentTime)); } } __Exemple : Le volume__\\ Le principe est le même que pour la trackbar, mais cet fois-ci pas besoin de lire le stdout.\\ Le petit détail ennuyant avec MPlayer est qu'il est impossible de lui donner une valeur à affecter au volume.\\ Il faut donc lui envoyer la commande "volume +" ou "volume -" le nombre de fois correspondant au volume désiré.\\ Lorsque le volume est modifié par l'utilisateur, une fonction doit être appelé, qui gère quelle instructions donner à MPlayer. void setVolume(int i) { int val = (i-volumeAncienneValeur); QString lecteurVolume; if((i-volAncienneValeur) >= 0){ for (int n=0;n<=val;n++) { lecteurVolume= "volume +1\n"; char *volumeProc=playerVolume.toAscii().data(); lecteurProcess->write(volumeProc); } } else { for (int n=0;n>=val;val++) { playerVolume = "volume -1\n"; char *volumeProc=playerVolume.toAscii().data(); lecteurProcess->write(volumeProc); } } volAncienneValeur=slider->sliderPosition(); ==La sauvegarde des paramètres de l'UI (save, load)== Pour éviter que l'utilisateur ait à remplir les champs de l'UI à chaque démarrage du logiciel, il est totalement possible de sauvegarder les valeurs de ces champs pendant une session, et de les réutiliser.\\ \\ Pour créer le fichier de config, on utilise le même principe que pour la config de Liquidsoap, c'est-à-dire avec [[https://doc.qt.io/archives/qt-4.8/qtextstream.html|QXmlStreamWriter]]. void makeConfig(QString chemin){ QFile file(chemin); file.open(QFile::WriteOnly | QFile::Text); QTextStream outStream(&file); outStream << "server_port = " << ui->Line_Local_Port->text() << endl; outStream << "server_pass = " << ui->Line_Local_Pass->text() << endl; [...] file.close; Cela produira un fichier contenant, ligne par ligne, les nom & valeurs que vous lui avez donné.\\ Note: N'oubliez pas de convertir vos types speciaux, tel qu'un Booléan, car ils pourraient mal se traduire en valeur brut.\\ \\ Maintenant, il faut lire ces valeurs, et les réattribués à leurs champs respectifs.\\ Ici, une [[https://doc.qt.io/archives/qt-4.8/qmap.html|QMap]] composé de deux QString nous simplifie grandement la tâche.\\ Le but est de lire l'intégralité du fichier, et dans ranger dans la QMap, chaque nom et sa valeur, ligne par ligne.\\ Puis on finit par remplir les champs en utilisant "settingsMap["//nomdemavaleur//"]". QMap settingsMap; void getConfig(QString chemin) { QFile file(chemin); QString current_line; file.open(QFile::ReadOnly | QFile::Text); QTextStream outStream(&file); while(!outStream.atEnd()){ std::string a = outStream.readLine().toStdString(); size_t pos = a.find('='); QString key = QString::fromStdString(a.substr(0, pos - 1)); QString value = QString::fromStdString(a.substr(pos + 2)); settingsMap[key] = value; } ui->Line_Local_Port->setText(settingsMap["server_port"]); ui->Line_Local_Pass->setText(settingsMap["server_pass"]); [...] file.close; ==== Installation Icestream v1.2 : ==== Pour l'installer télécharger les sources : [[http://apo33.org/pub/icestream/IceStream.tar.gz|IceStream ]] Les dépendances : sudo apt-get install mplayer qjackctl libvorbis-dev libxml2-dev libxslt1-dev qt4-qmake g++ libqt4-dev Puis les paquets en root : sudo su tar xvzf IceStream.tar.gz cd IceStream/ dpkg -i libshout_2.2-1_i386.deb dpkg -i libtheora_1.2-1_i386.deb dpkg -i icecast-2.3.2_3-1_i386.deb dpkg -i ices-2.0_4-1_i386.deb cd IHMB/ qmake -project qmake . make sudo cp IHMB /usr/local/bin/icestream ==== Utilisation : ==== Vous le lancer comme ceci en utilisateur ** simple** (id NOT sudo): icestream Pour l'utilisation vous pouvez suivre {{tutoicestream.pdf|ce tuto}} Enjoy !