Linphone

Aus Ottowiki
Wechseln zu: Navigation, Suche

Linphone ist ein freier VOIP Softwarestack und eine VOIP Anwendung zur Realisierung von Voice Over IP Kommunikation. Die Software enthält eine GTK+ basierte GUI-Applikation sowie Portierungen für diverse Smartphone-Plattformen.

Der Linphone-VOIP Stack umfasst drei Schichten:

Da im ATM Projekt Telefonieaudiodaten über eine Ethernetverbindung via RTP übertragen werden soll, kann Linphone ggf. als Basisimplementierung herangezogen werden. Eine nähere Analyse dieser Software wurde daher in Angriff genommen. Dieser Artikel fasst die diesbezüglich Erkenntnisse bzw. Installation, Portierung, interne Funktion sowie evtl. Defizite/Probleme zusammen.

Benötige Pakete installieren

sudo aptitude install libosip2-dev libexosip2-dev libspeex-dev libspeexdsp-dev libreadline-dev \
                      libgsm1-dev libavcodec-dev libswscale-dev libv4l-dev intltool libxv-dev

Die in Debian-Squeeze und vielen Debian-basierten Distributionen (Ubuntu) mitgelieferten Pakete "osip" und "exosip" sind nicht aktuell genug und müssen somit neu compiliert werden:

git clone git://git.savannah.gnu.org/osip.git
cd osip
git checkout -b 3.5.0 3.5.0
./autogen.sh
./configure
make
sudo make install
git clone git://git.savannah.nongnu.org/exosip.git
cd exosip
git checkout -b 3.5.0 3.5.0
./autogen.sh
./configure
make
sudo make install

Gleiches gilt für GTK+. Die in Debian stable (squeeze) enthaltene Version ist zu alt (<2.22), da die Funktion gtk_assistant_commit() nicht implementiert ist. Ggf. GTK Library aus der Testing-Distribution installieren oder neu übersetzen.

Kompilieren der Linphone Software

Das Linphonepakte wurde zur Übersetzung unter Debian-Linux angespasst und auf dem Akustik-Gitserver abgelegt. Die Installation und Übersetzung erfolgt mittels folgender Konsolekommandos unter einem Debian basierten System:

git clone git://simulacron/linphone.git --branch debian-squeeze --recursive
cd linphone
./autogen.sh
./configure
make

als Superuser:

make install

Die GTK+-Applikation kann danach über das Kommando "linphone" aus der Shell gestartet und getestet werden.

Synchronisation und Package Loss Concealment

Generell besteht bei Echtzeitdatenübertragung zwischen einem Sender- und Empfängergerät mit freilaufenden Taktgebern die Notwendigkeit, Datenströme zu synchronisieren, da in keinem Fall von einer exakten Übereinstimmung der Abtastfrequenzen von Quelle und Senke ausgegangen werden kann. Im TCB-Projekt wurde zudem innerhalb der Headunit ein mit 100ppm ausnehmend ungenauer Taktgeber eingesetzt. Im ungünstigsten Fall ergibt sich damit alle 200 Sekunden ein Frameüber- oder Unterlauf, siehe Diskussionseite. Im Falle der innerhalb des TCB-Projektes eingesetzten isochronen USB Datenströmen konnte die genannte Synchronisation mittels Beobachtung des Füllstandes des Sende- bzw. Empfangspuffers realisiert werden. Bei umfangreichen Höhrtests zeigte sich eine Unterteilung eines Telefoniesprachdatenpakets (20ms) in 4 Teilfragmente von jeweils 5ms als optimal. Bei Über- bzw. Unterlauf wurden jeweils zwei Teilfragemente mittels Crossfading verschmolzen.

Im Rahmen des LTE-NAD Projektes steht jedoch ein isochroner Übertragungsmechanismus nicht mehr zur Verfügung. Die Audiodaten werden stattdessen als UDP-Pakete übertragen. Der genaue Zeitpunkt ihrer Ankunft ist im Internetprotokoll nicht geregelt. Innerhalb der VOIP-Telefonie besteht zudem das dominantere Problem von nicht bzw. zu spät eintreffenden Paketen. Die entsprechenden Fehlstellen werden über das sogenannte Packet Loss Concealment maskiert. Im Falle eines langsamer als der Sendetaktgeber laufenden Quellentaktgebers kommt es ebenfalls zu einem Paketverlust, der über Packet Loss Concealment bereits behandelt wird. Im umgekehrten Fall kommt es nach frühestens 200 Sekunden zu einem Anwachsen bzw. Überlaufen des Jitterpuffers im Empfänger. Zur Klärung der genauen Verfahrensweise der bestehenden Linphone-Implmentierung wird im Folgenden der Quellcode daraufhin näher analysiert.

Zunächst ist festzuhalten, dass eine Sycnhronisierung der Audioinformation im Falle von UDP ausschließlich auf der Empfangsseite innerhalb bzw. nach erfolgter Dekodierung stattfinden kann, da die Sendeseite keinerlei Informationen über die Zustand des Empfängers kennt (kein Rückkanal). Die Initialisierung und Verarbeitung des Empfangsdatenstroms, Filter, etc. wird in der Verarbeitungsreihenfolge genannt:


  • RTP-Empfangsendpunkt, bekommet Speex oder G711 enkodierte Audiopakete und befüllt Queue (msrtp.c)
receiver_process()
{
  ...
  m = rtp_session_recvm_with_ts(d->session, timestamp) )
  ...
  ms_queue_put(f->outputs[0], m);
}
  • Dekodingfilter, leert Queue, dekodiert Audiopakete und leitet Package Loss Concealment bei fehlenden Paketen ein (msspeex.c)
dec_process()
{
  ...
  im=ms_queue_get(f->inputs[0])
  ...
  speex_bits_read_from(&bits,(char*)im->b_rptr,im->b_wptr-im->b_rptr);
  speex_decode_int(s->state,&bits,(int16_t*)om->b_wptr)
  ...
  ...
  if (s->plc && s->sample_time!=0 && f->ticker->time>=s->sample_time)
  {
    /* we should output a frame but no packet were decoded
        thus do packet loss concealment*/
    om=allocb(bytes,0);
    err=speex_decode_int(s->state,NULL,(int16_t*)om->b_wptr);
    ...
    ms_queue_put(f->outputs[0],om);
		
    s->sample_time+=20;
    s->plc_count++;
    if (s->plc_count>=plc_max) {
      s->sample_time=0;
    }
  }
}
  • Wird RTP-Eingangspuffer zu schnell gefüllt (Sendertakt >> Empfängertakt), so werden die Pakete nicht abgeholt
rtp_session_recvm_with_ts()
{
  ...
  ...
  ...

  /*calculate the stream timestamp from the user timestamp */
      ts = jitter_control_get_compensated_timestamp(&session->rtp.jittctl,user_ts);
      if (session->rtp.jittctl.enabled==TRUE)
       {
        if (session->permissive) // im Falle von Audiostreams immer 0
          mp = rtp_getq_permissive(&session->rtp.rq, ts,&rejected);
        else{
           mp = rtp_getq(&session->rtp.rq, ts,&rejected);
        }
      }
      else mp=getq(&session->rtp.rq);/*no jitter buffer at all*/

     ...
}
  • mit der Funktion rtp_getq()
mblk_t *rtp_getq(queue_t *q,uint32_t timestamp, int *rejected)
{
  ...	
  ...
  if (qempty(q))
  {
    /*ortp_debug("rtp_getq: q is empty.");*/
    return NULL;
  }
  /* return the packet with ts just equal or older than the asked timestamp */
  /* packets with older timestamps are discarded */
  while ((tmp=qfirst(q))!=NULL)
  {
    tmprtp=(rtp_header_t*)tmp->b_rptr;
    ortp_debug("rtp_getq: Seeing packet with ts=%i",tmprtp->timestamp);
    if ( RTP_TIMESTAMP_IS_NEWER_THAN(timestamp,tmprtp->timestamp) )
    {
      ...
    }
    else
    {
      break;
    }
  }
  return ret;
}
  • Innerhalb der Transportschicht wird die Empfangsqueue nicht mehr gefüllt, sobald die maximal erlaubt Anzahl von Paketen im Jitterbuffer überschritten ist. Per Default werden 60ms vorgegeben. Bei reinen Punkt zu Punkt Verbindungen sollte es jedoch im Hinblick auf ein kleineres Delay auch möglich sein, den Linphonestack auch ganz ohne Jitterbuffer zu betreiben.


Initialisierung

Die Initialisierung des gesamten VOIP-Stacks findet innehrlab der Linphoneapplikation innerhalb der Funktionen:

  • linphone_core_new() ->
  • linphone_core_init()

statt. Hier werden sämtliche Filter, RTP-Endpunkte, usw. konfiguriert. Kategory:Projekte