<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <font face="Helvetica, Arial, sans-serif">Hi All,<br>
      <br>
      I've got a client where I'm having some issues with CNG being
      received as part of a G.729 stream.  I'm using Monitor (Yes I'm
      aware the recommendation is to use MixMonitor, that presents
      licensing problems and max call concurrency we're trying to
      avoid).<br>
      <br>
      I'm busy trying to write a patch for format_g729 (attached) to
      better deal with CNG (and specifically I've got a scenario where
      there are some phones that refuse to honor annexb=no and still
      send CNG), and so far it seem to be OK'ish in that it'll ignore
      CNG packets, and perform the seek to ensure that the number of
      samples that asterisk core things has been written stays
      accurate.  However, this just caused more stuff that wasn't
      itching to start.  I'm attaching my patch.  It'll work under the
      assumption that main/channel.c will seek whenever it needs to
      write to a location in the output stream that's discontinuous to
      the position of the current stream, eg, if we've just written 320
      (40ms) of audio to the file, then receive CNG and then at sample
      offset 640 (80ms into the stream, assuming the previous 40ms was
      the start of the stream) receive another non-CNG frame containing
      actual voice data my assumption was that main/channel.c would
      SEEK_FORCECUR, +304 (it treats the two bytes of CNG as 16
      samples), so with 304 samples + 16 = 320 which is what we actually
      want to go forward, that would skip the appropriate 40ms to sample
      location 640 (80ms, or 80 bytes into the stream).<br>
      <br>
      However, looking at main/channel.c the seek seems to depend on how
      many samples has gone in the opposite direction for the channel?! 
      This doens't really make sense for me, but I may also be
      misinterpreting the code.  Specifically, when sending data to the
      write monitor (-out) this is the default code that applies (I'm
      only going to consider one direction here, but the same applies to
      both -in and -out), f is the frame we've just read (I'm going to
      make comments inline, hope that's OK):<br>
    </font><tt><br>
    </tt><tt>    if (ast_channel_monitor(chan) &&
      ast_channel_monitor(chan)->read_stream ) {</tt><tt><br>
    </tt><tt>        /* XXX what does this do ? */ <b><-- my
        question too :)</b></tt><tt><br>
    </tt><tt>#ifndef MONITOR_CONSTANT_DELAY<br>
              <b>This is the default case unless you edit the file
        higher up, so basically if we lag by more than X number<br>
                of samples in the opposite direction, apply a seek.<br>
      </b></tt><tt></tt><tt>        int jump = ast_channel_outsmpl(chan)
      - ast_channel_insmpl(chan) - 4 * f->samples;</tt><tt><br>
    </tt><tt>        if (jump >= 0) {</tt><tt><br>
    </tt><tt>            jump =
      calc_monitor_jump((ast_channel_outsmpl(chan) -
      ast_channel_insmpl(chan)),
      ast_format_rate(&f->subclass.format),
ast_format_rate(&ast_channel_monitor(chan)->read_stream->fmt->format));</tt><tt><br>
    </tt><tt>            if
      (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump,
      SEEK_FORCECUR) == -1) {</tt><tt><br>
    </tt><tt>                ast_log(LOG_WARNING, "Failed to perform
      seek in monitoring read stream, synchronization between the files
      may be broken\n");</tt><tt><br>
    </tt><tt>            }</tt><tt><br>
    </tt><tt>            ast_channel_insmpl_set(chan,
      ast_channel_insmpl(chan) + (ast_channel_outsmpl(chan) -
      ast_channel_insmpl(chan)) + f->samples);</tt><tt><br>
    </tt><tt>        } else {</tt><tt><br>
    </tt><tt>            ast_channel_insmpl_set(chan,
      ast_channel_insmpl(chan) + f->samples);</tt><tt><br>
    </tt><tt>        }</tt><tt><br>
    </tt><tt>#else</tt><tt><br>
    </tt><tt>        int jump =
      calc_monitor_jump((ast_channel_outsmpl(chan) -
      ast_channel_insmpl(chan)), ast_format_rate(f->subclass.codec),<br>
                 
ast_format_rate(ast_channel_monitor(chan)->read_stream->fmt->format));</tt><tt><br>
    </tt><tt>        if (jump - MONITOR_DELAY >= 0) { <b>This will
        seek if we're more than 150ms in terms of samples behind.</b></tt><tt><br>
    </tt><tt>            if
      (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump -
      f->samples, SEEK_FORCECUR) == -1)</tt><tt><br>
    </tt><tt>                ast_log(LOG_WARNING, "Failed to perform
      seek in monitoring read stream, synchronization between the files
      may be broken\n");</tt><tt><br>
    </tt><tt>            ast_channel_insmpl(chan) +=
      ast_channel_outsmpl(chan) - ast_channel_insmpl(chan);</tt><tt><br>
    </tt><tt>        } else</tt><tt><br>
    </tt><tt>            ast_channel_insmpl(chan) += f->samples;</tt><tt><br>
    </tt><tt>#endif</tt><tt><br>
    </tt><tt>        if (ast_channel_monitor(chan)->state ==
      AST_MONITOR_RUNNING) {</tt><tt><br>
    </tt><tt>            if
      (ast_writestream(ast_channel_monitor(chan)->read_stream, f)
      < 0)</tt><tt><br>
    </tt><tt>                ast_log(LOG_WARNING, "Failed to write data
      to channel monitor read stream\n");</tt><tt><br>
    </tt><tt>        }</tt><tt><br>
    </tt><tt>    }</tt><tt><br>
    </tt><font face="Helvetica, Arial, sans-serif"><br>
      Ok well, that's about as much as I understand of that code, and it
      makes very little to no sense to me actually.  As I understand it
      the offset into the file should be based somehow on the timestamp
      of the actual rtp, eg, when starting to send packets from a file
      asterisk will use a rtp timestamp value of (ignoring random
      offset) 0, then the following frame will have ts=160, and then
      ts=320 (based on a G729/8000).  Surely we can utilize these
      timestamps to calculate the offset into the file at which we want
      to be and then compare that with where we are?  Or even just store
      the previous timestamp + samples (assuming 1 timestamp unit
      implies 1 sample, which I'm not even sure of) and then if the next
      timestamp we receive differs, seek in the stream.  Going back to
      the previous example let's say we receive the following packets:<br>
      <br>
      seq=1,ts=0,len=20<br>
      seq=2,ts=160,len=20<br>
      seq=3,ts=320,len=2 <b>(CNG)</b><br>
      seq=4,ts=640,len=20<br>
      <br>
      Now, according to the calculations that I've seen going on packets
      1, 2 and 4 will have samples set to 160, and seq=3 will have
      samples=16, so after seq 1 we  can store 0+160, on receipt of
      seq=2 ts=160==160, so no seeking required, we can just write, for
      seq=3, the same applies (and my patch fakes the write of two bytes
      but ideally I'd actually like to let Monitor() know that I didn't
      write anything so that it can know it's still at ts=320, but for
      now it'll store next_expected_ts=320+16=336 since that's where it
      assumes we are, and on receipt of seq=4, 640!=336, and we seek
      forward 640-336=304 samples, or 38 bytes (keeping in mind the 16
      sample write has already been faked) so this will get us to byte
      80 in the file stream, at exactly the right location.<br>
      <br>
      What I don't know/understand is how things like hold will affect
      this, if at all?!  Also, for the -out case it seems not all values
      for the ast_frame is set before being written to the stream, from
      my log:<br>
      <br>
          -- IAX2/ulsvoip-jkroon-A1-16492 is making progress passing it
      to SIP/101-00000001<br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=0),
      frame(datalen=20,samples=160,ts=0,len=0,seqno=0)<br>
      <br>
      That's fine, but note that len and ts is 0.<br>
      <br>
      Sent RTP packet to      192.168.47.68:11784 (type 18, seq 061496,
      ts 000000, len 000020)<br>
      <br>
      And here it's actually sent (rtp debug).<br>
      <br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=0,samples=160,ts=0,len=0,seqno=0)<br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=0,samples=160,ts=0,len=0,seqno=0)<br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=0,samples=160,ts=0,len=0,seqno=0)<br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=0,samples=160,ts=0,len=0,seqno=0)<br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=0,samples=160,ts=0,len=0,seqno=0)<br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=0,samples=160,ts=0,len=0,seqno=0)<br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=0,samples=160,ts=0,len=0,seqno=0)<br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=0,samples=160,ts=0,len=0,seqno=0)<br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=0,samples=160,ts=0,len=0,seqno=0)<br>
      [Jan 28 14:02:16] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=0,samples=160,ts=0,len=0,seqno=0)<br>
      <br>
      All of this feels wrong, ie, we're writing 160 samples, but no
      actual data ?!  Nor is RTP actually being sent?<br>
      <br>
             > 0xe374bc0 -- Probation passed - setting RTP source
      address to 192.168.47.68:11784<br>
      Got  RTP packet from    192.168.47.68:11784 (type 18, seq 000375,
      ts 517557, len 000002)<br>
      <br>
      Receipt of RTP packet.  And ts is some large (random?) number.  As
      I understand from this point it should increment at a rate of
      8kHz.<br>
      <br>
      [Jan 28 14:02:17] WARNING[21280][C-00000001]: format_g729.c:148
      g729_seek:
      g729_seek(/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-in,
      1760, SEEK_FORCECUR), cur=0, offset=220<br>
      <br>
      Ok, so I've no idea what the in/outsmpl values are at this point
      but I assume outsmpl=11*160=1760, which is where the seek value
      comes from (insmpl=0?), and the offset to which is being seeked is
      correct.  My alternate suggestion may very well actually break
      this come to think of it.<br>
      <br>
      [Jan 28 14:02:17] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-in,pos=220),
      frame(datalen=2,samples=16,ts=64694,len=2,seqno=375)<br>
      [Jan 28 14:02:17] WARNING[21280][C-00000001]: format_g729.c:104
      g729_write: Probable CNG parameter update detected, this is a bug
      in the sender who should not be sending CNG updates - try and
      switch off Voice Activity Detected (VAD) or Silence Suppression on
      the sender.<br>
      <br>
      And the write, and the CNG warning triggers, letting us know that
      we're receiving CNG and that the peer most likely has VAD (Silence
      suppression) enabled, and isn't honoring annexb=no.  It is a bug
      in the peer, but I'd still like to do what I can to not bump into
      problems.  So my patch will simply seek forward two bytes here,
      incorrectly so assuming that a following seek will attempt to seek
      again to get us back onto an 80-sample boundary (10ms ==
      80samples, and whilst usually ptime=20 it can be other values,
      including 10ms, which results in 80 samples per RTP voice frame). 
      After this write the write position will be 222.<br>
      <br>
      Got  RTP packet from    192.168.47.68:11784 (type 18, seq 000376,
      ts 517957, len 000002)<br>
      [Jan 28 14:02:17] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-in,pos=222),
      frame(datalen=2,samples=16,ts=64744,len=2,seqno=376)<br>
      <br>
      According to the RTP timestamp here we've moved 400 samples ahead
      (or 50ms).  This is not divisable by 160 as per the original
      expectation from format_g729, so the patch fixes that too.  So
      since the previous write "wrote" 16 samples I'd expect a seek of
      +384 samples, which would result in a forward seek of 48 bytes,
      getting to 270 offset into the file - which is where we want to be
      for the next write.<br>
      <br>
      [Jan 28 14:02:17] WARNING[21280][C-00000001]: format_g729.c:113
      g729_write: Writing a G729A frame to the wrong offset <br>
      (pos=222,ts=64744,len=2,datalen=140265041952770,writelen=0).  This
      is indicative of a bug, please contact <a class="moz-txt-link-abbreviated" href="mailto:jaco@uls.co.za">jaco@uls.co.za</a> for
      assistance.<br>
    </font><br>
    <font face="Helvetica, Arial, sans-serif"><font face="Helvetica,
        Arial, sans-serif">And as a result of the seek not happening
        this warning triggers.<br>
      </font><br>
      [Jan 28 14:02:17] WARNING[21280][C-00000001]: format_g729.c:87
      g729_write:
      filestream(filename=/var/spool/asterisk/monitor/2014-01-28/2014-01-28_140212-greyscale-1390910532.2-101-0845158255-out,pos=20),
      frame(datalen=20,samples=160,ts=0,len=0,seqno=0)<br>
      Sent RTP packet to      192.168.47.68:11784 (type 18, seq 061497,
      ts 004360, len 000020)<br>
      <br>
      And here we're still writing to output position 20, ie, in spite
      of no data having been presented on the previous writes we most
      likely should have seeked ahead for 160 samples for each write. 
      The lack of ts= and len= being filled out makes this hard to
      assess.<br>
      <br>
      For those that's wondering, the reason for not using MixMonitor in
      this case is actually primarily to avoid issues with licensing of
      the G.729 codec.  The cost starts adding up pretty quickly.<br>
    </font>
    <div class="moz-signature">-- <br>
      Kind Regards,<br>
      Jaco Kroon<br>
      <img src="cid:part1.05080003.09040501@uls.co.za" usemap="#Map"
        style="color:white" border="0" width="530" height="100">
      <map name="Map" id="Map">
        <area shape="rect" coords="441,19,460,36"
          href="https://www.facebook.com/ultimatelinuxsolutions">
        <area shape="rect" coords="441,39,458,57"
          href="http://news.uls.co.za/">
        <area shape="rect" coords="354,62,461,73"
          href="http://www.uls.co.za/">
      </map>
    </div>
  </body>
</html>