[Git][pkg-voip-team/asterisk][debian/latest] 10 commits: embed project asterisk-opus as component
Jonas Smedegaard (@js)
gitlab at salsa.debian.org
Fri Feb 4 00:12:31 GMT 2022
Jonas Smedegaard pushed to branch debian/latest at Debian VoIP Packaging Team / asterisk
Commits:
5e1ff3e8 by Jonas Smedegaard at 2022-02-04T00:26:27+01:00
embed project asterisk-opus as component
- - - - -
d1bc32b2 by Jonas Smedegaard at 2022-02-04T00:33:43+01:00
New upstream version 16.23.0~dfsg+~cs6.10.40431411
- - - - -
be6c4a47 by Jonas Smedegaard at 2022-02-04T00:34:30+01:00
Update upstream source from tag 'upstream/16.23.0_dfsg+_cs6.10.40431411'
Update to upstream version '16.23.0~dfsg+~cs6.10.40431411'
with Debian dir b1670d74b23d280fa9ffaf56966ef6f33ffc18d8
- - - - -
19e7cbfb by Jonas Smedegaard at 2022-02-04T00:35:04+01:00
update copyright info: update coverage
- - - - -
cac5a3ca by Jonas Smedegaard at 2022-02-04T00:35:04+01:00
add patches 2015 2016 to integrate opus module with asterisk; integrate opus module with build rules; have asterisk-modules replace and break asterisk-opus
- - - - -
ae82b3a8 by Jonas Smedegaard at 2022-02-04T00:35:04+01:00
relax to build-depend unversioned on libjansson-dev: required version satisfied in all supported Debian releases
- - - - -
078a3c65 by Jonas Smedegaard at 2022-02-04T00:35:04+01:00
build-depend on libneon27-dev (not libneon27-gnutls-dev, with libneon27-dev only as fallback); drop fallback build-dependencies for libcurl4-openssl-dev libradcli-dev
- - - - -
1a43dd23 by Jonas Smedegaard at 2022-02-04T00:35:04+01:00
build-depend on liblua5.2-dev (not liblua5.1-0-dev)
- - - - -
0927d99a by Jonas Smedegaard at 2022-02-04T00:35:04+01:00
build-depend on libcodec2-dev libfftw3-dev libsndfile1-dev
- - - - -
75ccadcc by Jonas Smedegaard at 2022-02-04T00:42:59+01:00
prepare for release: update changelog and copyright hints
- - - - -
22 changed files:
- + Xopus/LICENSE
- + Xopus/Makefile
- + Xopus/README.md
- + Xopus/asterisk.patch
- + Xopus/codecs/codec_opus_open_source.c
- + Xopus/codecs/ex_opus.h
- + Xopus/enable_native_plc.patch
- + Xopus/formats/format_ogg_opus_open_source.c
- + Xopus/formats/format_vp8.c
- + Xopus/include/asterisk/opus.h
- + Xopus/res/res_format_attr_opus.c
- debian/changelog
- debian/clean
- debian/control
- debian/copyright
- debian/copyright_hints
- debian/gbp.conf
- + debian/patches/2015_opus.patch
- + debian/patches/2016_opus_plc.patch
- debian/patches/series
- debian/rules
- debian/watch
Changes:
=====================================
Xopus/LICENSE
=====================================
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it..
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
=====================================
Xopus/Makefile
=====================================
@@ -0,0 +1,60 @@
+prefix=/usr/local
+exec_prefix=$(prefix)
+libdir=$(exec_prefix)/lib
+
+# build with `make OPUSENC=0` to disable rewrite support using libopusenc
+OPUSENC?=1
+
+CFLAGS=-pthread -D_FORTIFY_SOURCE=2 -fPIC
+DEBUG=-g3
+OPTIMIZE=-O3
+CPPFLAGS=
+DEFS=
+INSTALL=/usr/bin/install -c
+LDFLAGS=-pthread -Wl,--warn-common
+LIBS=
+SHELL=/bin/sh
+
+ASTMODDIR=$(libdir)/asterisk/modules
+MODULES=codec_opus_open_source format_ogg_opus_open_source format_vp8 res_format_attr_opus
+
+.SUFFIXES: .c .so
+
+.PHONY: all clean install uninstall $(MODULES)
+
+all: $(MODULES)
+
+clean:
+ rm -f */*.so
+
+install: $(MODULES)
+ $(INSTALL) -D -t $(DESTDIR)$(ASTMODDIR) */*.so
+
+uninstall:
+ cd $(ASTMODDIR) && rm -f $(addsuffix .so,$(MODULES))
+
+codec_opus_open_source: LIBS+=-lopus
+codec_opus_open_source: DEFS+=-DAST_MODULE=\"codec_opus_open_source\" \
+ -DAST_MODULE_SELF_SYM=__internal_codec_opus_open_source_self
+codec_opus_open_source: codecs/codec_opus_open_source.so
+
+format_ogg_opus_open_source: CPATH+=-I/usr/include/opus
+format_ogg_opus_open_source: LIBS+=-lopus -lopusfile
+format_ogg_opus_open_source: DEFS+=-DAST_MODULE=\"format_ogg_opus_open_source\" \
+ -DAST_MODULE_SELF_SYM=__internal_format_ogg_opus_open_source_self
+ifeq ($(OPUSENC),1)
+format_ogg_opus_open_source: LIBS+=-lopusenc
+format_ogg_opus_open_source: DEFS+=-DHAVE_OPUSENC
+endif
+format_ogg_opus_open_source: formats/format_ogg_opus_open_source.so
+
+format_vp8: DEFS+=-DAST_MODULE=\"format_vp8\" \
+ -DAST_MODULE_SELF_SYM=__internal_format_vp8_self
+format_vp8: formats/format_vp8.so
+
+res_format_attr_opus: DEFS+=-DAST_MODULE=\"res_format_attr_opus\" \
+ -DAST_MODULE_SELF_SYM=__internal_res_format_attr_opus_self
+res_format_attr_opus: res/res_format_attr_opus.so
+
+.c.so:
+ $(CC) -o $@ $(CPATH) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(DEBUG) $(OPTIMIZE) $(LIBS) -shared $(LDFLAGS) $<
=====================================
Xopus/README.md
=====================================
@@ -0,0 +1,94 @@
+# Opus Codec for Asterisk
+
+Opus is the default audio codec in WebRTC. WebRTC is available in Asterisk via SIP over WebSockets (WSS). Nevertheless, Opus can be used for other transports (UDP, TCP, TLS) as well. Opus supersedes previous codecs like CELT and SiLK. Furthermore in favor of Opus, other open-source audio codecs are no longer developed, like Speex, iSAC, iLBC, and Siren. If you use your Asterisk as a back-to-back user agent (B2BUA) and you transcode between various audio codecs, one should enable Opus for future compatibility.
+
+Since Asterisk 13.12 (and Asterisk 14.0.1), Opus is not only supported for pass-through but can be transcoded as well. This allows you to translate to/from other audio codecs like those for landline telephones (ISDN: G.711; DECT: G.726-32; and HD: G.722) or mobile phones (GSM, AMR, AMR-WB, 3GPP EVS). This can be achieved by
+
+A. enabling `codec_opus` via `make menuselect`, or
+
+B. downloading the module from [Digium Downloads](http://www.digium.com/products/asterisk/downloads) » Add-on Voice Codecs, or
+
+C. downloading the module [directly](http://downloads.digium.com/pub/telephony/codec_opus/).
+
+That way, you get a binary module, which is closed software. Here, this repository offers Open Source Software. In contrast to a binary module, Open Source Software allows you to double-check the existing code, contribute and/or add your own features.
+
+This repository is for Asterisk 13 and newer. If you still use Asterisk 11, please, [continue there…](https://github.com/meetecho/asterisk-opus) however, that variant does not offer negotiation of SDP parameters (fmtp). If you need that, please, upgrade to Asterisk 13.7 or newer.
+
+## Installing
+At least Asterisk 13.7 is required. These changes were last tested with Asterisk 13.17, 14.6, and 15.0. If you use a newer version and transcoding fails, please, [report](https://help.github.com/articles/creating-an-issue/)!
+
+ cd /usr/src/
+ wget downloads.asterisk.org/pub/telephony/asterisk/asterisk-13-current.tar.gz
+ tar zxf ./asterisk*
+ cd ./asterisk*
+ sudo apt-get --assume-yes install build-essential autoconf libssl-dev libncurses-dev libnewt-dev libxml2-dev libsqlite3-dev uuid-dev libjansson-dev libblocksruntime-dev xmlstarlet
+
+Install libraries:
+
+To support transcoding, you’ll need to install an Opus library, for example in Debian/Ubuntu:
+
+ sudo apt-get --assume-yes install libopusfile-dev
+
+Apply all changes:
+
+ wget github.com/traud/asterisk-opus/archive/asterisk-13.7.tar.gz
+ tar zxf ./master.tar.gz
+ rm ./master.tar.gz
+ cp --verbose ./asterisk-opus*/include/asterisk/* ./include/asterisk
+ cp --verbose ./asterisk-opus*/codecs/* ./codecs
+ cp --verbose ./asterisk-opus*/res/* ./res
+
+(Optionally) apply the patch for File Formats (untested):
+
+Two format modules are added which allow you to play VP8 and Ogg Opus files without transcoding.
+
+ cp --verbose ./asterisk-opus*/formats/* ./formats
+ patch -p1 <./asterisk-opus*/asterisk.patch
+
+(Optionally) apply the patch for Native PLC (experimental):
+
+Out of the box, Asterisk does not detect lost (or late) RTP packets. Such a detection is required to conceal lost packets (PLC). PLC improves situations like Wi-Fi Roaming or mobile-phone handovers. This patch detects lost/late packets but is experimental. If your scenario requires PLC and you find an issue with this patch, please, continue with [ASTERISK-25629…](http://issues.asterisk.org/jira/browse/ASTERISK-25629)
+
+ patch -p1 <./asterisk-opus*/enable_native_plc.patch
+
+Run the bootstrap script to re-generate configure:
+
+ ./bootstrap.sh
+
+Configure your patched Asterisk:
+
+ ./configure
+
+Enable slin16 in menuselect for transcoding, for example via:
+
+ make menuselect.makeopts
+ ./menuselect/menuselect --enable-category MENUSELECT_CORE_SOUNDS
+
+Compile and install:
+
+ make
+ sudo make install
+
+Alternatively, you can use the Makefile of this repository to create just the shared libraries of the modules. That way, you do not have to (re-) make your whole Asterisk.
+
+## Testing
+Opus is the default audio codec in WebRTC. Therefore, you can use Mozilla Firefox or Google Chrome via SIP over WebSockets in Asterisk. However, many traditional apps (SIP over UDP) added Opus as well. Simply add `allow=opus` in your configuration file `sip.conf` and the SIP channel driver `chan_sip` is able to negotiate Opus via SDP.
+
+However, when you use a traditional VoIP app, please, double-check with your app vendor, whether [RFC 7587](http://tools.ietf.org/html/rfc7587) is known and supported. Many Opus enabled apps still use outdated SDP parameters (fmtp) and have not updated to the latest version of this RFC from June 2015. Furthermore, there are still apps which do not handle a `rtpmap` with `/2` at the end, because those apps do not know that Opus advertises two channels in SDP always. Some apps do not increment the RTP timestamps correctly. Finally, I am aware of just a few apps which allow you to tailor Opus for your bandwidth needs. Or stated differently: Many apps use fullband all the time. This increases the bitrate unnecessarily, if the attached microphone, earpiece, or loudspeaker do not support that frequency range. Tests revealed that Asterisk might not even force a lower bandwidth because many apps do not honor the send SDP parameters. Consequently with such an app, users tend to go for older audio codecs because they experience no benefit.
+
+The app [Acrobits Softphone](http://itunes.apple.com/app/id314192799?mt=8) for Apple iOS lets you tailor the bandwidth and therefore recommended for your initial tests. Because the current app situation is like that, do not forget to allow legacy audio codecs even [SiLK 12 kHz](https://github.com/traud/asterisk-silk) and [iLBC 20](https://github.com/traud/asterisk-silk). If you are interested not in music but just in voice, you might even consider to prefer older wideband audio-codecs like G.722 (landline telephones) and [AMR-WB](https://github.com/traud/asterisk-amr) (mobile-operator gateway).
+
+## What is missing
+* `codecs.conf`: Instead, you have to change the file `include/asterisk/opus.h` and re-make Asterisk. The binary module from Digium supports the configuration file `codecs.conf`.
+* Forward Error Correction (FEC) based on the actual packet loss reported by the remote party via RTCP, called Adaptive FEC. FreeSWITCH offers Opus with FEC.
+* Packetization Time `ptime` of the channel driver is unknown to the Opus encoder. Therefore, Asterisk is going to create 20 ms despite the negotiated amount of frames. A high ptime is useful only for low bitrates.
+
+This transcoding module works for me and contains everything I need. If you cannot code yourself, however, you need one of these or even another feature, please, [report](https://help.github.com/articles/creating-an-issue/).
+
+
+## Thanks go to
+* [Opus team](http://www.opus-codec.org/contact/),
+* Ron Lee packaging the libraries for Debian/Ubuntu,
+* [Lorenzo Miniero](https://github.com/meetecho/asterisk-opus) created the original code for Asterisk 11,
+* [Tzafrir Cohen](http://issues.asterisk.org/jira/browse/ASTERISK-21981) drove the pass-through support for Asterisk 13,
+* [Sean Bright](https://github.com/seanbright/asterisk-opus) ported the transcoding code over to Asterisk 13, added many changes directly into Asterisk 13, and maintained the port until Asterisk 13.11.
\ No newline at end of file
=====================================
Xopus/asterisk.patch
=====================================
@@ -0,0 +1,37 @@
+--- a/build_tools/menuselect-deps.in
++++ b/build_tools/menuselect-deps.in
+@@ -44,2 +44,3 @@
+ OPUS=@PBX_OPUS@
++OPUSENC=@PBX_OPUSENC@
+ OPUSFILE=@PBX_OPUSFILE@
+--- a/configure.ac
++++ b/configure.ac
+@@ -523,2 +523,3 @@
+ AST_EXT_LIB_SETUP([OPUS], [Opus], [opus])
++AST_EXT_LIB_SETUP([OPUSENC], [Opusenc], [opusenc])
+ AST_EXT_LIB_SETUP([OPUSFILE], [Opusfile], [opusfile])
+@@ -2524,2 +2525,3 @@
+ fi
++AST_PKG_CONFIG_CHECK(OPUSENC, libopusenc)
+ AST_EXT_LIB_CHECK([OPUSFILE], [opusfile], [op_open_callbacks], [opus/opusfile.h], [], [$__opus_include])
+--- a/makeopts.in
++++ b/makeopts.in
+@@ -228,6 +228,9 @@
+ OPUS_INCLUDE=@OPUS_INCLUDE@
+ OPUS_LIB=@OPUS_LIB@
+
++OPUSENC_INCLUDE=@OPUSENC_INCLUDE@
++OPUSENC_LIB=@OPUSENC_LIB@
++
+ OPUSFILE_INCLUDE=@OPUSFILE_INCLUDE@
+ OPUSFILE_LIB=@OPUSFILE_LIB@
+
+--- formats/Makefile
++++ formats/Makefile
+@@ -12,4 +12,6 @@
+ -include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
++_ASTCFLAGS+=-DASTERISK_VERSION_NUM=${ASTERISKVERSIONNUM}
++
+ MODULE_PREFIX=format
+ MENUSELECT_CATEGORY=FORMATS
=====================================
Xopus/codecs/codec_opus_open_source.c
=====================================
@@ -0,0 +1,853 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Lorenzo Miniero
+ *
+ * Lorenzo Miniero <lorenzo at meetecho.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Translate between signed linear and Opus (Open Codec)
+ *
+ * \author Lorenzo Miniero <lorenzo at meetecho.com>
+ *
+ * \note This work was motivated by Mozilla
+ *
+ * \ingroup codecs
+ *
+ * \extref http://www.opus-codec.org/docs/html_api-1.1.0/
+ *
+ */
+
+/*** MODULEINFO
+ <depend>opus</depend>
+ <conflict>codec_opus</conflict>
+ <defaultenabled>yes</defaultenabled>
+***/
+
+#include "asterisk.h"
+
+#if defined(ASTERISK_REGISTER_FILE)
+ASTERISK_REGISTER_FILE()
+#elif defined(ASTERISK_FILE_VERSION)
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")
+#endif
+
+#include "asterisk/astobj2.h" /* for ao2_ref */
+#include "asterisk/cli.h" /* for ast_cli_entry, ast_cli, etc */
+#include "asterisk/codec.h" /* for ast_codec_get */
+#include "asterisk/format.h" /* for ast_format_get_attribute_data */
+#include "asterisk/frame.h" /* for ast_frame, etc */
+#include "asterisk/linkedlists.h" /* for AST_LIST_NEXT, etc */
+#include "asterisk/lock.h" /* for ast_atomic_fetchadd_int */
+#include "asterisk/logger.h" /* for ast_log, LOG_ERROR, etc */
+#include "asterisk/module.h"
+#include "asterisk/translate.h" /* for ast_trans_pvt, etc */
+#include "asterisk/utils.h" /* for ARRAY_LEN */
+
+#include <opus/opus.h>
+
+#include "asterisk/opus.h" /* for CODEC_OPUS_DEFAULT_* */
+
+#define BUFFER_SAMPLES 5760
+#define MAX_CHANNELS 2
+#define OPUS_SAMPLES 960
+
+/* Sample frame data */
+#include "asterisk/slin.h"
+#include "ex_opus.h"
+
+static struct codec_usage {
+ int encoder_id;
+ int decoder_id;
+ int encoders;
+ int decoders;
+} usage;
+
+/*
+ * Stores the function pointer 'sample_count' of the cached ast_codec
+ * before this module was loaded. Allows to restore this previous
+ * function pointer, when this module in unloaded.
+ */
+static struct ast_codec *opus_codec; /* codec of the cached format */
+static int (*opus_samples_previous)(struct ast_frame *frame);
+
+/* Private structures */
+struct opus_coder_pvt {
+ void *opus; /* May be encoder or decoder */
+ int sampling_rate;
+ int multiplier;
+ int id;
+ int16_t buf[BUFFER_SAMPLES];
+ int framesize;
+ int inited;
+ int channels;
+ int decode_fec_incoming;
+ int previous_lost;
+};
+
+struct opus_attr {
+ unsigned int maxbitrate;
+ unsigned int maxplayrate;
+ unsigned int unused; /* was minptime */
+ unsigned int stereo;
+ unsigned int cbr;
+ unsigned int fec;
+ unsigned int dtx;
+ unsigned int spropmaxcapturerate; /* FIXME: not utilised, yet */
+ unsigned int spropstereo; /* FIXME: currently, we are just mono */
+};
+
+/* Helper methods */
+static int opus_encoder_construct(struct ast_trans_pvt *pvt, int sampling_rate)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+ struct opus_attr *attr = pvt->explicit_dst ? ast_format_get_attribute_data(pvt->explicit_dst) : NULL;
+ const opus_int32 bitrate = attr ? attr->maxbitrate : CODEC_OPUS_DEFAULT_BITRATE;
+ const int maxplayrate = attr ? attr->maxplayrate : CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE;
+ const int channels = attr ? attr->stereo + 1 : CODEC_OPUS_DEFAULT_STEREO + 1;
+ const opus_int32 vbr = attr ? !(attr->cbr) : !CODEC_OPUS_DEFAULT_CBR;
+ const opus_int32 fec = attr ? attr->fec : CODEC_OPUS_DEFAULT_FEC;
+ const opus_int32 dtx = attr ? attr->dtx : CODEC_OPUS_DEFAULT_DTX;
+ const int application = OPUS_APPLICATION_VOIP;
+ int status = 0;
+
+ opvt->opus = opus_encoder_create(sampling_rate, channels, application, &status);
+
+ if (status != OPUS_OK) {
+ ast_log(LOG_ERROR, "Error creating the Opus encoder: %s\n", opus_strerror(status));
+ return -1;
+ }
+
+ if (sampling_rate <= 8000 || maxplayrate <= 8000) {
+ status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
+ } else if (sampling_rate <= 12000 || maxplayrate <= 12000) {
+ status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND));
+ } else if (sampling_rate <= 16000 || maxplayrate <= 16000) {
+ status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
+ } else if (sampling_rate <= 24000 || maxplayrate <= 24000) {
+ status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND));
+ } /* else we use the default: OPUS_BANDWIDTH_FULLBAND */
+
+ if (0 < bitrate && bitrate != 510000) {
+ status = opus_encoder_ctl(opvt->opus, OPUS_SET_BITRATE(bitrate));
+ } /* else we use the default: OPUS_AUTO */
+ status = opus_encoder_ctl(opvt->opus, OPUS_SET_VBR(vbr));
+ status = opus_encoder_ctl(opvt->opus, OPUS_SET_INBAND_FEC(fec));
+ status = opus_encoder_ctl(opvt->opus, OPUS_SET_DTX(dtx));
+
+ opvt->sampling_rate = sampling_rate;
+ opvt->multiplier = 48000 / sampling_rate;
+ opvt->framesize = sampling_rate / 50;
+ opvt->id = ast_atomic_fetchadd_int(&usage.encoder_id, 1) + 1;
+
+ ast_atomic_fetchadd_int(&usage.encoders, +1);
+
+ ast_debug(3, "Created encoder #%d (%d -> opus)\n", opvt->id, sampling_rate);
+
+ return 0;
+}
+
+static int opus_decoder_construct(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+ /* struct opus_attr *attr = ast_format_get_attribute_data(f->subclass.format); */
+ int error = 0;
+
+ opvt->sampling_rate = pvt->t->dst_codec.sample_rate;
+ opvt->multiplier = 48000 / opvt->sampling_rate;
+ opvt->channels = /* attr ? attr->spropstereo + 1 :*/ 1; /* FIXME */
+
+ opvt->opus = opus_decoder_create(opvt->sampling_rate, opvt->channels, &error);
+
+ if (error != OPUS_OK) {
+ ast_log(LOG_ERROR, "Error creating the Opus decoder: %s\n", opus_strerror(error));
+ return -1;
+ }
+
+ opvt->id = ast_atomic_fetchadd_int(&usage.decoder_id, 1) + 1;
+
+ ast_atomic_fetchadd_int(&usage.decoders, +1);
+
+ ast_debug(3, "Created decoder #%d (opus -> %d)\n", opvt->id, opvt->sampling_rate);
+
+ return 0;
+}
+
+/* Translator callbacks */
+static int lintoopus_new(struct ast_trans_pvt *pvt)
+{
+ return opus_encoder_construct(pvt, pvt->t->src_codec.sample_rate);
+}
+
+static int opustolin_new(struct ast_trans_pvt *pvt)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+
+ opvt->previous_lost = 0; /* we are new and have not lost anything */
+ opvt->inited = 0; /* we do not know the "sprop" values, yet */
+
+ return 0;
+}
+
+static int lintoopus_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+
+ /* XXX We should look at how old the rest of our stream is, and if it
+ is too old, then we should overwrite it entirely, otherwise we can
+ get artifacts of earlier talk that do not belong */
+ memcpy(opvt->buf + pvt->samples, f->data.ptr, f->datalen);
+ pvt->samples += f->samples;
+
+ return 0;
+}
+
+static struct ast_frame *lintoopus_frameout(struct ast_trans_pvt *pvt)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+ struct ast_frame *result = NULL;
+ struct ast_frame *last = NULL;
+ int samples = 0; /* output samples */
+
+ while (pvt->samples >= opvt->framesize) {
+ /* status is either error or output bytes */
+ const int status = opus_encode(opvt->opus,
+ opvt->buf + samples,
+ opvt->framesize,
+ pvt->outbuf.uc,
+ BUFFER_SAMPLES);
+
+ samples += opvt->framesize;
+ pvt->samples -= opvt->framesize;
+
+ if (status < 0) {
+ ast_log(LOG_ERROR, "Error encoding the Opus frame: %s\n", opus_strerror(status));
+ } else {
+ struct ast_frame *current = ast_trans_frameout(pvt,
+ status,
+ OPUS_SAMPLES);
+
+ if (!current) {
+ continue;
+ } else if (last) {
+ AST_LIST_NEXT(last, frame_list) = current;
+ } else {
+ result = current;
+ }
+ last = current;
+ }
+ }
+
+ /* Move the data at the end of the buffer to the front */
+ if (samples) {
+ memmove(opvt->buf, opvt->buf + samples, pvt->samples * 2);
+ }
+
+ return result;
+}
+
+static int opustolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+ int decode_fec;
+ int frame_size;
+ opus_int16 *dst;
+ opus_int32 len;
+ unsigned char *src;
+ int status;
+
+ if (!opvt->inited && f->datalen == 0) {
+ return 0; /* we cannot start without data */
+ } else if (!opvt->inited) { /* 0 < f->datalen */
+ status = opus_decoder_construct(pvt, f);
+ opvt->inited = 1;
+ if (status) {
+ return status;
+ }
+ }
+
+ /*
+ * When we get a frame indicator (ast_null_frame), format is NULL. Because FEC
+ * status can change any time (SDP re-negotiation), we save again and again.
+ */
+ if (f->subclass.format) {
+ struct opus_attr *attr = ast_format_get_attribute_data(f->subclass.format);
+
+ if (attr) {
+ opvt->decode_fec_incoming = attr->fec;
+ }
+ }
+ decode_fec = opvt->decode_fec_incoming;
+
+ /*
+ * The Opus Codec, actually its library allows
+ * - Forward-Error Correction (FEC), and
+ * - native Packet-Loss Concealment (PLC).
+ * The sender might include FEC. If there is no FEC, because it was not send
+ * or the FEC data got lost, the API of the Opus library does PLC instead.
+ * Therefore we have three boolean variables:
+ * - current frame got lost: f->datalen == 0,
+ * - previous frame got lost: opvt->previous_lost, and
+ * - FEC negotiated on SDP layer: decode_fec.
+ * Now, we go through all cases. Because some cases use the same source code
+ * we have less than 8 (2^3) cases.
+ *
+ * Some notes on the coding style of this section:
+ * This code section is passed for each incoming frame, normally every
+ * 20 milliseconds. For each channel, this code is passed individually.
+ * Therefore, this code should be as performant as possible. On the other
+ * hand, PLC plus FEC is complicated. Therefore, code readability is one
+ * prerequisite to understand, debug, and review this code section. Because
+ * we do have optimising compilers, we are able to sacrify optimised code
+ * for code readability. If you find an error or unnecessary calculation
+ * which is not optimised = removed by your compiler, please, create an
+ * issue on <https://github.com/traud/asterisk-opus/issues>. I am just
+ * a human and human do mistakes. However, humans love to learn.
+ *
+ * Source-code examples are
+ * - <https://git.xiph.org/?p=opus.git;a=history;f=src/opus_demo.c>,
+ * - <https://freeswitch.org/stash/projects/FS/repos/freeswitch/browse/src/mod/codecs/mod_opus/mod_opus.c>
+ * and the official mailing list itself:
+ * <https://www.google.de/search?q=site:lists.xiph.org+opus>.
+ */
+
+ /* Case 1 and 2 */
+ if (f->datalen == 0 && opvt->previous_lost) {
+ /*
+ * If this frame and the previous frame got lost, we do not have any
+ * data for FEC. Therefore, we go for PLC on the previous frame. However,
+ * the next frame could include FEC for the currently lost frame.
+ * Therefore, we "wait" for the next frame to fix the current frame.
+ */
+ decode_fec = 0; /* = do PLC */
+ opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size));
+ dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels);
+ len = 0;
+ src = NULL;
+ status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
+ if (status < 0) {
+ ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
+ } else {
+ pvt->samples += status;
+ pvt->datalen += status * opvt->channels * sizeof(int16_t);
+ }
+ /*
+ * Save the state of the current frame, whether it is lost = "wait".
+ * That way, we are able to decide whether to do FEC next time.
+ */
+ opvt->previous_lost = (f->datalen == 0 || status < 0);
+ return 0;
+ }
+
+ /* Case 3 */
+ if (f->datalen == 0 && !decode_fec) { /* !opvt->previous_lost */
+ /*
+ * The sender stated in SDP: "I am not going to provide FEC". Therefore,
+ * we do not wait for the next frame and do PLC right away.
+ */
+ decode_fec = 0;
+ opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size));
+ dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels);
+ len = f->datalen;
+ src = NULL;
+ status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
+ if (status < 0) {
+ ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
+ } else {
+ pvt->samples += status;
+ pvt->datalen += status * opvt->channels * sizeof(int16_t);
+ }
+ opvt->previous_lost = (f->datalen == 0 || status < 0);
+ return 0;
+ }
+
+ /* Case 4 */
+ if (f->datalen == 0) { /* decode_fec && !opvt->previous_lost */
+ /*
+ * The previous frame was of no issue. Therefore, we do not have to
+ * reconstruct it. We do not have any data in the current frame but the
+ * sender might give us FEC with the next frame. We cannot do anything
+ * but wait for the next frame. Till Asterisk 13.7, this creates the
+ * warning "opustolin48 did not update samples 0". Please, ignore this
+ * warning or apply the patch included in the GitHub repository.
+ */
+ status = 0; /* no samples to add currently */
+ if (status < 0) {
+ ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
+ } else {
+ pvt->samples += status;
+ pvt->datalen += status * opvt->channels * sizeof(int16_t);
+ }
+ opvt->previous_lost = (f->datalen == 0 || status < 0);
+ return 0;
+ }
+
+ /* Case 5 and 6 */
+ if (!opvt->previous_lost) { /* 0 < f->datalen */
+ /*
+ * The perfect case - the previous frame was not lost and we have data
+ * in the current frame. Therefore, neither FEC nor PLC are required.
+ */
+ decode_fec = 0;
+ frame_size = BUFFER_SAMPLES / opvt->multiplier; /* parse everything */
+ dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels);
+ len = f->datalen;
+ src = f->data.ptr;
+ status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
+ if (status < 0) {
+ ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
+ } else {
+ pvt->samples += status;
+ pvt->datalen += status * opvt->channels * sizeof(int16_t);
+ }
+ opvt->previous_lost = (f->datalen == 0 || status < 0);
+ return 0;
+ }
+
+ /* Case 7 */
+ if (!decode_fec) { /* 0 < f->datalen && opvt->previous_lost */
+ /*
+ * The previous frame got lost and the sender stated in SDP: "I am not
+ * going to provide FEC". Therefore, we do PLC. Furthermore, we try to
+ * decode the current frame because we have data. This creates jitter
+ * because we create double the amount of frames as normal, see
+ * <https://issues.asterisk.org/jira/browse/ASTERISK-25483>. If this is
+ * an issue for your use-case, please, file and issue report on
+ * <https://github.com/traud/asterisk-opus/issues>.
+ */
+ decode_fec = 0;
+ opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size));
+ dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels);
+ len = 0;
+ src = NULL;
+ status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
+ if (status < 0) {
+ ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
+ } else {
+ pvt->samples += status;
+ pvt->datalen += status * opvt->channels * sizeof(int16_t);
+ }
+ decode_fec = 0;
+ frame_size = BUFFER_SAMPLES / opvt->multiplier; /* parse everything */
+ dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); /* append after PLC data */
+ len = f->datalen;
+ src = f->data.ptr;
+ status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
+ if (status < 0) {
+ ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
+ } else {
+ pvt->samples += status;
+ pvt->datalen += status * opvt->channels * sizeof(int16_t);
+ }
+ opvt->previous_lost = (f->datalen == 0 || status < 0);
+ return 0;
+ }
+
+ /* Case 8; Last Case */
+ { /* 0 < f->datalen && opvt->previous_lost && decode_fec */
+ decode_fec = 1;
+ opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size));
+ dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels);
+ len = f->datalen;
+ src = f->data.ptr;
+ status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
+ if (status < 0) {
+ ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
+ } else {
+ pvt->samples += status;
+ pvt->datalen += status * opvt->channels * sizeof(int16_t);
+ }
+ decode_fec = 0;
+ frame_size = BUFFER_SAMPLES / opvt->multiplier; /* parse everything */
+ dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); /* append after FEC data */
+ len = f->datalen;
+ src = f->data.ptr;
+ status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
+ if (status < 0) {
+ ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
+ } else {
+ pvt->samples += status;
+ pvt->datalen += status * opvt->channels * sizeof(int16_t);
+ }
+ opvt->previous_lost = (f->datalen == 0 || status < 0);
+ return 0;
+ }
+}
+
+static void lintoopus_destroy(struct ast_trans_pvt *arg)
+{
+ struct opus_coder_pvt *opvt = arg->pvt;
+
+ if (!opvt || !opvt->opus) {
+ return;
+ }
+
+ opus_encoder_destroy(opvt->opus);
+ opvt->opus = NULL;
+
+ ast_atomic_fetchadd_int(&usage.encoders, -1);
+
+ ast_debug(3, "Destroyed encoder #%d (%d->opus)\n", opvt->id, opvt->sampling_rate);
+}
+
+static void opustolin_destroy(struct ast_trans_pvt *arg)
+{
+ struct opus_coder_pvt *opvt = arg->pvt;
+
+ if (!opvt || !opvt->opus) {
+ return;
+ }
+
+ opus_decoder_destroy(opvt->opus);
+ opvt->opus = NULL;
+
+ ast_atomic_fetchadd_int(&usage.decoders, -1);
+
+ ast_debug(3, "Destroyed decoder #%d (opus->%d)\n", opvt->id, opvt->sampling_rate);
+}
+
+static char *handle_cli_opus_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct codec_usage copy;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "opus show";
+ e->usage =
+ "Usage: opus show\n"
+ " Displays Opus encoder/decoder utilization.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2) {
+ return CLI_SHOWUSAGE;
+ }
+
+ copy = usage;
+
+ ast_cli(a->fd, "%d/%d encoders/decoders are in use.\n", copy.encoders, copy.decoders);
+
+ return CLI_SUCCESS;
+}
+
+/* Translators */
+static struct ast_translator opustolin = {
+ .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP,
+ .name = "opustolin",
+ .src_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 8000,
+ },
+ .format = "slin",
+ .newpvt = opustolin_new,
+ .framein = opustolin_framein,
+ .destroy = opustolin_destroy,
+ .sample = opus_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = (BUFFER_SAMPLES / (48000 / 8000)) * 2, /* because of possible FEC */
+ .buf_size = (BUFFER_SAMPLES / (48000 / 8000)) * MAX_CHANNELS * sizeof(opus_int16) * 2,
+ .native_plc = 1,
+};
+
+static struct ast_translator lintoopus = {
+ .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP,
+ .name = "lintoopus",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 8000,
+ },
+ .dst_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "opus",
+ .newpvt = lintoopus_new,
+ .framein = lintoopus_framein,
+ .frameout = lintoopus_frameout,
+ .destroy = lintoopus_destroy,
+ .sample = slin8_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator opustolin12 = {
+ .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 1,
+ .name = "opustolin12",
+ .src_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 12000,
+ },
+ .format = "slin12",
+ .newpvt = opustolin_new,
+ .framein = opustolin_framein,
+ .destroy = opustolin_destroy,
+ .sample = opus_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = (BUFFER_SAMPLES / (48000 / 12000)) * 2, /* because of possible FEC */
+ .buf_size = (BUFFER_SAMPLES / (48000 / 12000)) * MAX_CHANNELS * sizeof(opus_int16) * 2,
+ .native_plc = 1,
+};
+
+static struct ast_translator lin12toopus = {
+ .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 1,
+ .name = "lin12toopus",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 12000,
+ },
+ .dst_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "opus",
+ .newpvt = lintoopus_new,
+ .framein = lintoopus_framein,
+ .frameout = lintoopus_frameout,
+ .destroy = lintoopus_destroy,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator opustolin16 = {
+ .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 2,
+ .name = "opustolin16",
+ .src_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 16000,
+ },
+ .format = "slin16",
+ .newpvt = opustolin_new,
+ .framein = opustolin_framein,
+ .destroy = opustolin_destroy,
+ .sample = opus_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = (BUFFER_SAMPLES / (48000 / 16000)) * 2, /* because of possible FEC */
+ .buf_size = (BUFFER_SAMPLES / (48000 / 16000)) * MAX_CHANNELS * sizeof(opus_int16) * 2,
+ .native_plc = 1,
+};
+
+static struct ast_translator lin16toopus = {
+ .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 2,
+ .name = "lin16toopus",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 16000,
+ },
+ .dst_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "opus",
+ .newpvt = lintoopus_new,
+ .framein = lintoopus_framein,
+ .frameout = lintoopus_frameout,
+ .destroy = lintoopus_destroy,
+ .sample = slin16_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator opustolin24 = {
+ .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 4,
+ .name = "opustolin24",
+ .src_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 24000,
+ },
+ .format = "slin24",
+ .newpvt = opustolin_new,
+ .framein = opustolin_framein,
+ .destroy = opustolin_destroy,
+ .sample = opus_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = (BUFFER_SAMPLES / (48000 / 24000)) * 2, /* because of possible FEC */
+ .buf_size = (BUFFER_SAMPLES / (48000 / 24000)) * MAX_CHANNELS * sizeof(opus_int16) * 2,
+ .native_plc = 1,
+};
+
+static struct ast_translator lin24toopus = {
+ .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 4,
+ .name = "lin24toopus",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 24000,
+ },
+ .dst_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "opus",
+ .newpvt = lintoopus_new,
+ .framein = lintoopus_framein,
+ .frameout = lintoopus_frameout,
+ .destroy = lintoopus_destroy,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator opustolin48 = {
+ .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 8,
+ .name = "opustolin48",
+ .src_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "slin48",
+ .newpvt = opustolin_new,
+ .framein = opustolin_framein,
+ .destroy = opustolin_destroy,
+ .sample = opus_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES * 2, /* twice, because of possible FEC */
+ .buf_size = BUFFER_SAMPLES * MAX_CHANNELS * sizeof(opus_int16) * 2,
+ .native_plc = 1,
+};
+
+static struct ast_translator lin48toopus = {
+ .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 8,
+ .name = "lin48toopus",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "opus",
+ .newpvt = lintoopus_new,
+ .framein = lintoopus_framein,
+ .frameout = lintoopus_frameout,
+ .destroy = lintoopus_destroy,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_cli_entry cli[] = {
+ AST_CLI_DEFINE(handle_cli_opus_show, "Display Opus codec utilization.")
+};
+
+static int opus_samples(struct ast_frame *frame)
+{
+ opus_int32 sampling_rate = 48000; /* FIXME */
+
+ return opus_packet_get_nb_samples(frame->data.ptr, frame->datalen, sampling_rate);
+}
+
+static int reload(void)
+{
+ /* Reload does nothing */
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ opus_codec->samples_count = opus_samples_previous;
+ ao2_ref(opus_codec, -1);
+
+ res = ast_unregister_translator(&opustolin);
+ res |= ast_unregister_translator(&lintoopus);
+ res |= ast_unregister_translator(&opustolin12);
+ res |= ast_unregister_translator(&lin12toopus);
+ res |= ast_unregister_translator(&opustolin16);
+ res |= ast_unregister_translator(&lin16toopus);
+ res |= ast_unregister_translator(&opustolin24);
+ res |= ast_unregister_translator(&lin24toopus);
+ res |= ast_unregister_translator(&opustolin48);
+ res |= ast_unregister_translator(&lin48toopus);
+
+ ast_cli_unregister_multiple(cli, ARRAY_LEN(cli));
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ opus_codec = ast_codec_get("opus", AST_MEDIA_TYPE_AUDIO, 48000);
+ opus_samples_previous = opus_codec->samples_count;
+ opus_codec->samples_count = opus_samples;
+
+ res = ast_register_translator(&opustolin);
+ res |= ast_register_translator(&lintoopus);
+ res |= ast_register_translator(&opustolin12);
+ res |= ast_register_translator(&lin12toopus);
+ res |= ast_register_translator(&opustolin16);
+ res |= ast_register_translator(&lin16toopus);
+ res |= ast_register_translator(&opustolin24);
+ res |= ast_register_translator(&lin24toopus);
+ res |= ast_register_translator(&opustolin48);
+ res |= ast_register_translator(&lin48toopus);
+
+ ast_cli_register_multiple(cli, ARRAY_LEN(cli));
+
+ return res;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Opus Coder/Decoder",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
=====================================
Xopus/codecs/ex_opus.h
=====================================
@@ -0,0 +1,38 @@
+/*! \file
+ * \brief 8-bit data
+ *
+ * Copyright (C) 2014, Lorenzo Miniero
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+#include "asterisk/format_cache.h" /* for ast_format_opus */
+#include "asterisk/frame.h" /* for ast_frame, etc */
+
+/* Opus, a 20ms sample */
+static uint8_t ex_opus[] = {
+ 0x4b, 0x41, 0x25, 0x0b, 0xe4, 0x55, 0xc6, 0x74,
+ 0xda, 0xbb, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static struct ast_frame *opus_sample(void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .datalen = sizeof(ex_opus),
+ .samples = OPUS_SAMPLES,
+ .mallocd = 0,
+ .offset = 0,
+ .src = __PRETTY_FUNCTION__,
+ .data.ptr = ex_opus,
+ };
+
+ f.subclass.format = ast_format_opus;
+
+ return &f;
+}
=====================================
Xopus/enable_native_plc.patch
=====================================
@@ -0,0 +1,156 @@
+--- a/main/translate.c (Asterisk 13.17.2)
++++ b/main/translate.c (working copy)
+@@ -359,4 +359,5 @@
+ pvt->f.src = pvt->t->name;
+ pvt->f.data.ptr = pvt->outbuf.c;
++ pvt->f.seqno = 0x10000;
+
+ /*
+@@ -531,11 +532,44 @@
+ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume)
+ {
++ const unsigned int rtp_seqno_max_value = 0xffff;
+ struct ast_trans_pvt *p = path;
+- struct ast_frame *out;
++ struct ast_frame *out_last, *out = NULL;
+ struct timeval delivery;
+ int has_timing_info;
+ long ts;
+ long len;
+- int seqno;
++ int seqno, frames_missing;
++
++ /* Determine the amount of lost packets for PLC */
++ /* But not at start with first frame = path->f.seqno is still 0x10000 */
++ /* But not when there is no sequence number = frame created internally */
++ if ((path->f.seqno <= rtp_seqno_max_value) && (path->f.seqno != f->seqno)) {
++ if (f->seqno < path->f.seqno) { /* seqno overrun situation */
++ frames_missing = rtp_seqno_max_value + f->seqno - path->f.seqno - 1;
++ } else {
++ frames_missing = f->seqno - path->f.seqno - 1;
++ }
++ /* Out-of-order packet - more precise: late packet */
++ if ((rtp_seqno_max_value + 1) / 2 < frames_missing) {
++ if (consume) {
++ ast_frfree(f);
++ }
++ /*
++ * Do not pass late packets to any transcoding module, because that
++ * confuses the state of any library (packets inter-depend). With
++ * the next packet, this one is going to be treated as lost packet.
++ */
++ return NULL;
++ }
++
++ if (frames_missing > 96) {
++ struct ast_str *str = ast_str_alloca(256);
++
++ /* not DEBUG but NOTICE because of WARNING in main/cannel.c:__ast_queue_frame */
++ ast_log(LOG_NOTICE, "%d lost frame(s) %d/%d %s\n", frames_missing, f->seqno, path->f.seqno, ast_translate_path_to_str(path, &str));
++ }
++ } else {
++ frames_missing = 0;
++ }
+
+ has_timing_info = ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO);
+@@ -567,16 +601,91 @@
+ }
+ delivery = f->delivery;
+- for (out = f; out && p ; p = p->next) {
+- struct ast_frame *current = out;
+
+- do {
+- framein(p, current);
+- current = AST_LIST_NEXT(current, frame_list);
+- } while (current);
+- if (out != f) {
+- ast_frfree(out);
++ for (out_last = NULL; frames_missing + 1; frames_missing--) {
++ struct ast_frame *frame_to_translate, *inner_head;
++ struct ast_frame missed = {
++ .frametype = AST_FRAME_VOICE,
++ .subclass.format = f->subclass.format,
++ .datalen = 0,
++ /* In RTP, the amount of samples might change anytime */
++ /* If that happened while frames got lost, what to do? */
++ .samples = f->samples, /* FIXME */
++ .src = __FUNCTION__,
++ .data.uint32 = 0,
++ .delivery.tv_sec = 0,
++ .delivery.tv_usec = 0,
++ .flags = 0,
++ /* RTP sequence number is between 0x0001 and 0xffff */
++ .seqno = (rtp_seqno_max_value + 1 + f->seqno - frames_missing) & rtp_seqno_max_value,
++ };
++
++ if (frames_missing) {
++ frame_to_translate = &missed;
++ } else {
++ frame_to_translate = f;
++ }
++
++ /* The translation path from one format to another might contain several steps */
++ /* out* collects the result for missed frame(s) and input frame(s) */
++ /* out is the result of the conversion of all frames, translated into the destination format */
++ /* out_last is the last frame in that list, to add frames faster */
++ for (p = path, inner_head = frame_to_translate; inner_head && p; p = p->next) {
++ struct ast_frame *current, *inner_last, *inner_prev = frame_to_translate;
++
++ /* inner* collects the result of each conversion step, the input for the next step */
++ /* inner_head is a list of frames created by each conversion step */
++ /* inner_last is the last frame in that list, to add frames faster */
++ for (inner_last = NULL, current = inner_head; current; current = AST_LIST_NEXT(current, frame_list)) {
++ struct ast_frame *tmp;
++
++ framein(p, current);
++ tmp = p->t->frameout(p);
++
++ if (!tmp) {
++ continue;
++ } else if (inner_last) {
++ struct ast_frame *t;
++
++ /* Determine the last frame of the list before appending to it */
++ while ((t = AST_LIST_NEXT(inner_last, frame_list))) {
++ inner_last = t;
++ }
++ AST_LIST_NEXT(inner_last, frame_list) = tmp;
++ } else {
++ inner_prev = inner_head;
++ inner_head = tmp;
++ inner_last = tmp;
++ }
++ }
++
++ /* The current step did not create any frames = no frames for the next step */
++ /* The steps are not lost because framein buffered those for the next input frame */
++ if (!inner_last) {
++ inner_prev = inner_head;
++ inner_head = NULL;
++ }
++ if (inner_prev != frame_to_translate) {
++ ast_frfree(inner_prev); /* Frees just the intermediate lists */
++ }
++ }
++
++ /* This frame created no frames after translation = continue with next frame */
++ /* The frame is not lost because framein buffered it to be combined with the next frame */
++ if (!inner_head) {
++ continue;
++ } else if (out_last) {
++ struct ast_frame *t;
++
++ /* Determine the last frame of the list before appending to it */
++ while ((t = AST_LIST_NEXT(out_last, frame_list))) {
++ out_last = t;
++ }
++ AST_LIST_NEXT(out_last, frame_list) = inner_head;
++ } else {
++ out = inner_head;
++ out_last = inner_head;
+ }
+- out = p->t->frameout(p);
+ }
++
+ if (out) {
+ /* we have a frame, play with times */
=====================================
Xopus/formats/format_ogg_opus_open_source.c
=====================================
@@ -0,0 +1,420 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+ <depend>opusfile</depend>
+ <use type="external">opusenc</use>
+ <conflict>format_ogg_opus</conflict>
+ <defaultenabled>yes</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+#if defined(ASTERISK_REGISTER_FILE)
+ASTERISK_REGISTER_FILE()
+#elif defined(ASTERISK_FILE_VERSION)
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")
+#endif
+
+#include <fcntl.h> /* for SEEK_CUR, SEEK_END, SEEK_SET */
+
+#include "asterisk/format_cache.h" /* for ast_format_slin48 */
+#include "asterisk/frame.h" /* for ast_frame, AST_FRIENDLY_OFFSET */
+#include "asterisk/logger.h" /* for ast_log, LOG_ERROR, AST_LOG_ERROR */
+#include "asterisk/mod_format.h" /* for ast_filestream, ast_format_def */
+#include "asterisk/module.h"
+#if defined(HAVE_OPUSENC)
+#include "asterisk/config.h" /* for ast_variable, ast_config_destroy */
+#include "asterisk/format.h" /* for ast_format_get_... */
+#include "asterisk/utils.h" /* for ast_flags */
+#endif
+
+#include <opus/opus.h>
+#include <opus/opusfile.h>
+#if defined(HAVE_OPUSENC)
+#include <opus/opusenc.h>
+#endif
+
+#include "asterisk/opus.h"
+
+/* 120ms of 48KHz audio */
+#define SAMPLES_MAX 5760
+#define BUF_SIZE (2 * SAMPLES_MAX)
+
+#if defined(HAVE_OPUSENC)
+/* Variables that can be set in formats.conf */
+static int complexity = 10; /* OPUS default */
+static int maxbitrate = CODEC_OPUS_DEFAULT_BITRATE;
+#endif
+
+struct ogg_opus_desc {
+ OggOpusFile *of;
+
+#if defined(HAVE_OPUSENC)
+ OggOpusEnc *enc;
+ OggOpusComments *comments;
+#endif
+
+ size_t writing;
+ off_t writing_pcm_pos;
+};
+
+static int fread_wrapper(void *_stream, unsigned char *_ptr, int _nbytes)
+{
+ FILE *stream = _stream;
+ size_t bytes_read;
+
+ if (!stream || _nbytes < 0) {
+ return -1;
+ }
+
+ bytes_read = fread(_ptr, 1, _nbytes, stream);
+
+ return bytes_read > 0 || feof(stream) ? (int) bytes_read : OP_EREAD;
+}
+
+static int fseek_wrapper(void *_stream, opus_int64 _offset, int _whence)
+{
+ FILE *stream = _stream;
+
+ return fseeko(stream, (off_t) _offset, _whence);
+}
+
+static opus_int64 ftell_wrapper(void *_stream)
+{
+ FILE *stream = _stream;
+
+ return ftello(stream);
+}
+
+static int ogg_opus_open(struct ast_filestream *s)
+{
+ struct ogg_opus_desc *desc = (struct ogg_opus_desc *) s->_private;
+ OpusFileCallbacks cb = {
+ .read = fread_wrapper,
+ .seek = fseek_wrapper,
+ .tell = ftell_wrapper,
+ .close = NULL,
+ };
+
+ memset(desc, 0, sizeof(*desc));
+ desc->of = op_open_callbacks(s->f, &cb, NULL, 0, NULL);
+ if (!desc->of) {
+ return -1;
+ }
+
+ return 0;
+}
+
+#if defined(HAVE_OPUSENC)
+static int fwrite_wrapper(void *user_data, const unsigned char *ptr, opus_int32 len)
+{
+ FILE *stream = user_data;
+
+ return fwrite(ptr, 1, len, stream) != (size_t) len;
+}
+
+static int fclose_wrapper(void *user_data)
+{
+ return 0;
+}
+
+static const OpusEncCallbacks enc_callbacks = {
+ .write = fwrite_wrapper,
+ .close = fclose_wrapper,
+};
+
+static int ogg_opus_rewrite(struct ast_filestream *fs, const char *comment)
+{
+ struct ogg_opus_desc *desc = fs->_private;
+ int err, rate, channels, family;
+
+ desc->writing = 1;
+ desc->writing_pcm_pos = 0;
+
+ desc->comments = ope_comments_create();
+ ope_comments_add(desc->comments, "ENCODER", "Asterisk PBX");
+ if (comment)
+ ope_comments_add(desc->comments, "COMMENT", comment);
+
+ rate = ast_format_get_sample_rate(fs->fmt->format);
+#if defined(ASTERISK_VERSION_NUM) && (ASTERISK_VERSION_NUM < 150000)
+ channels = 1;
+#else
+ channels = ast_format_get_channel_count(fs->fmt->format);
+#endif
+ if (channels < 3) {
+ family = 0;
+ } else {
+ family = 1;
+ }
+
+ desc->enc = ope_encoder_create_callbacks(&enc_callbacks, fs->f, desc->comments, rate, channels, family, &err);
+
+ if (!desc->enc) {
+ ast_log(AST_LOG_ERROR, "Error creating the OGG/Opus encoder: %s\n", ope_strerror(err));
+ return -1;
+ }
+
+ ope_encoder_ctl(desc->enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
+ ope_encoder_ctl(desc->enc, OPUS_SET_COMPLEXITY(complexity));
+ ope_encoder_ctl(desc->enc, OPUS_SET_BITRATE(maxbitrate));
+
+ return 0;
+}
+
+static int ogg_opus_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ struct ogg_opus_desc *desc = fs->_private;
+ int err;
+
+ if (!desc->writing) {
+ ast_log(LOG_ERROR, "This OGG/Opus stream is not set up for writing!\n");
+ return -1;
+ }
+
+ if (!f->datalen) {
+ return -1;
+ }
+
+ err = ope_encoder_write(desc->enc, f->data.ptr, f->samples);
+ if (err) {
+ ast_log(AST_LOG_ERROR, "Error encoding OGG/Opus frame: %s\n", ope_strerror(err));
+ return -1;
+ }
+
+ desc->writing_pcm_pos += f->samples;
+ return 0;
+}
+#else
+static int ogg_opus_rewrite(struct ast_filestream *fs, const char *comment)
+{
+ ast_log(LOG_ERROR, "Writing OGG/Opus streams is not built-in\n");
+ return -1;
+}
+
+static int ogg_opus_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ ast_log(LOG_ERROR, "Writing OGG/Opus streams is not built-in\n");
+ return -1;
+}
+#endif
+
+static int ogg_opus_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ int seek_result = -1;
+ off_t relative_pcm_pos;
+ struct ogg_opus_desc *desc = fs->_private;
+
+ if (desc->writing) {
+ return -1;
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ seek_result = op_pcm_seek(desc->of, sample_offset);
+ break;
+ case SEEK_CUR:
+ if ((relative_pcm_pos = op_pcm_tell(desc->of)) < 0) {
+ seek_result = -1;
+ break;
+ }
+ seek_result = op_pcm_seek(desc->of, relative_pcm_pos + sample_offset);
+ break;
+ case SEEK_END:
+ if ((relative_pcm_pos = op_pcm_total(desc->of, -1)) < 0) {
+ seek_result = -1;
+ break;
+ }
+ seek_result = op_pcm_seek(desc->of, relative_pcm_pos - sample_offset);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unknown *whence* to seek on OGG/Opus streams!\n");
+ break;
+ }
+
+ /* normalize error value to -1,0 */
+ return (seek_result == 0) ? 0 : -1;
+}
+
+static int ogg_opus_trunc(struct ast_filestream *fs)
+{
+ return -1;
+}
+
+static off_t ogg_opus_tell(struct ast_filestream *fs)
+{
+ struct ogg_opus_desc *desc = fs->_private;
+ off_t pos;
+
+ if (desc->writing) {
+ return desc->writing_pcm_pos / CODEC_OPUS_DEFAULT_SAMPLE_RATE * DEFAULT_SAMPLE_RATE;
+ }
+
+ pos = (off_t) op_pcm_tell(desc->of);
+ if (pos < 0) {
+ return -1;
+ }
+ return pos;
+}
+
+static struct ast_frame *ogg_opus_read(struct ast_filestream *fs, int *whennext)
+{
+ struct ogg_opus_desc *desc = fs->_private;
+ int hole = 1;
+ int samples_read;
+ opus_int16 *out_buf;
+
+ if (desc->writing) {
+ ast_log(LOG_WARNING, "Reading is not supported on OGG/Opus in writing mode.\n");
+ return NULL;
+ }
+
+ AST_FRAME_SET_BUFFER(&fs->fr, fs->buf, AST_FRIENDLY_OFFSET, BUF_SIZE)
+
+ out_buf = (opus_int16 *) fs->fr.data.ptr;
+
+ while (hole) {
+ samples_read = op_read(
+ desc->of,
+ out_buf,
+ SAMPLES_MAX,
+ NULL);
+
+ if (samples_read != OP_HOLE) {
+ hole = 0;
+ }
+ }
+
+ if (samples_read <= 0) {
+ return NULL;
+ }
+
+ fs->fr.datalen = samples_read * 2;
+ fs->fr.samples = samples_read;
+ *whennext = fs->fr.samples;
+
+ return &fs->fr;
+}
+
+static void ogg_opus_close(struct ast_filestream *fs)
+{
+ struct ogg_opus_desc *desc = fs->_private;
+
+ if (desc->writing) {
+#if defined(HAVE_OPUSENC)
+ ope_encoder_drain(desc->enc);
+ ope_encoder_destroy(desc->enc);
+ ope_comments_destroy(desc->comments);
+#endif
+ } else {
+ op_free(desc->of);
+ }
+}
+
+static struct ast_format_def opus_f = {
+ .name = "ogg_opus",
+ .exts = "opus",
+ .open = ogg_opus_open,
+ .rewrite = ogg_opus_rewrite,
+ .write = ogg_opus_write,
+ .seek = ogg_opus_seek,
+ .trunc = ogg_opus_trunc,
+ .tell = ogg_opus_tell,
+ .read = ogg_opus_read,
+ .close = ogg_opus_close,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct ogg_opus_desc),
+};
+
+static int parse_config(int reload)
+{
+#if defined(HAVE_OPUSENC)
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_config *cfg = ast_config_load("formats.conf", config_flags);
+ struct ast_variable *var;
+ int i, res = 0;
+
+ if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
+ return res;
+ }
+
+ for (var = ast_variable_browse(cfg, "opus"); var; var = var->next) {
+ if (!strcasecmp(var->name, "complexity")) {
+ i = atoi(var->value);
+ if (i < 0 || i > 10) {
+ res = 1;
+ ast_log(LOG_ERROR, "Complexity must be in 0-10\n");
+ break;
+ }
+
+ complexity = i;
+ } else if (!strcasecmp(var->name, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE)) {
+ i = atoi(var->value);
+ if (i < 500 || i > 512000) {
+ res = 1;
+ ast_log(LOG_ERROR, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE " must be in 500-512000\n");
+ break;
+ }
+
+ maxbitrate = i;
+ }
+ }
+ ast_config_destroy(cfg);
+
+ return res;
+#else
+ return 0;
+#endif
+}
+
+static int load_module(void)
+{
+ if (parse_config(0)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ opus_f.format = ast_format_slin48;
+ if (ast_format_def_register(&opus_f)) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int reload_module(void)
+{
+ if (parse_config(1)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_def_unregister(opus_f.name);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "OGG/Opus audio",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .reload = reload_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND
+);
=====================================
Xopus/formats/format_vp8.c
=====================================
@@ -0,0 +1,211 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Lorenzo Miniero
+ *
+ * Lorenzo Miniero <lorenzo at meetecho.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Save to raw, headerless VP8 data.
+ *
+ * \author Lorenzo Miniero <lorenzo at meetecho.com>
+ *
+ * \note Basically a "clone" of the H.264 passthrough format
+ *
+ * \arg File name extension: VP8
+ * \ingroup formats
+ * \arg See \ref AstVideo
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+***/
+
+#include "asterisk.h"
+
+#if defined(ASTERISK_REGISTER_FILE)
+ASTERISK_REGISTER_FILE()
+#elif defined(ASTERISK_FILE_VERSION)
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")
+#endif
+
+#include <netinet/in.h> /* for htonl, htons, ntohl, ntohs */
+#include <sys/time.h> /* for timeval = ast_filestream->ast_frame.delivery */
+
+#include "asterisk/format_cache.h" /* for ast_format_vp8 */
+#include "asterisk/frame.h" /* for ast_frame, AST_FRIENDLY_OFFSET */
+#include "asterisk/logger.h" /* for ast_log, LOG_WARNING */
+#include "asterisk/mod_format.h" /* for ast_filestream, ast_format_def */
+#include "asterisk/module.h"
+
+/* VP8 passthrough */
+#define FRAME_ENDED 0x8000
+
+#define BUF_SIZE 4096
+struct vp8_desc {
+ unsigned int lastts;
+};
+
+static int vp8_open(struct ast_filestream *s)
+{
+ unsigned int ts;
+
+ if (fread(&ts, 1, sizeof(ts), s->f) < sizeof(ts)) {
+ ast_log(LOG_WARNING, "Empty file!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct ast_frame *vp8_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+ int mark = 0;
+ unsigned short len;
+ unsigned int ts;
+ struct vp8_desc *fs = (struct vp8_desc *) s->_private;
+
+ /* Send a frame from the file to the appropriate channel */
+ if ((res = fread(&len, 1, sizeof(len), s->f)) < 1) {
+ return NULL;
+ }
+
+ len = ntohs(len);
+ mark = (len & FRAME_ENDED) ? 1 : 0;
+ len &= 0x7fff;
+ if (len > BUF_SIZE) {
+ ast_log(LOG_WARNING, "Length %d is too long\n", len);
+ len = BUF_SIZE; /* XXX truncate */
+ }
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, len)
+ if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
+ if (res) {
+ ast_log(LOG_WARNING, "Short read (%d of %d) (%s)!\n", res, len, strerror(errno));
+ }
+ return NULL;
+ }
+ s->fr.samples = fs->lastts;
+ s->fr.datalen = len;
+ s->fr.subclass.frame_ending = mark;
+ s->fr.delivery.tv_sec = 0;
+ s->fr.delivery.tv_usec = 0;
+ if ((res = fread(&ts, 1, sizeof(ts), s->f)) == sizeof(ts)) {
+ fs->lastts = ntohl(ts);
+ *whennext = fs->lastts * 4 / 45;
+ } else {
+ *whennext = 0;
+ }
+ return &s->fr;
+}
+
+static int vp8_write(struct ast_filestream *s, struct ast_frame *f)
+{
+ int res;
+ unsigned int ts;
+ unsigned short len;
+ int mark;
+
+ if (f->frametype != AST_FRAME_VIDEO) {
+ ast_log(LOG_WARNING, "Asked to write non-video frame!\n");
+ return -1;
+ }
+
+ mark = f->subclass.frame_ending ? FRAME_ENDED : 0;
+ ts = htonl(f->samples);
+ if ((res = fwrite(&ts, 1, sizeof(ts), s->f)) != sizeof(ts)) {
+ ast_log(LOG_WARNING, "Bad write (%d/4): %s\n", res, strerror(errno));
+ return -1;
+ }
+
+ len = htons(f->datalen | mark);
+ if ((res = fwrite(&len, 1, sizeof(len), s->f)) != sizeof(len)) {
+ ast_log(LOG_WARNING, "Bad write (%d/2): %s\n", res, strerror(errno));
+ return -1;
+ }
+
+ if ((res = fwrite(f->data.ptr, 1, f->datalen, s->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int vp8_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ /* No way Jose */
+ return -1;
+}
+
+static int vp8_trunc(struct ast_filestream *fs)
+{
+ int fd;
+ off_t cur;
+
+ if ((fd = fileno(fs->f)) < 0) {
+ ast_log(LOG_WARNING, "Unable to determine file descriptor for VP8 filestream %p: %s\n", fs, strerror(errno));
+ return -1;
+ }
+
+ if ((cur = ftello(fs->f)) < 0) {
+ ast_log(LOG_WARNING, "Unable to determine current position in VP8 filestream %p: %s\n", fs, strerror(errno));
+ return -1;
+ }
+
+ /* Truncate file to current length */
+ return ftruncate(fd, cur);
+}
+
+static off_t vp8_tell(struct ast_filestream *fs)
+{
+ off_t offset = ftell(fs->f);
+ return offset; /* XXX totally bogus, needs fixing */
+}
+
+static struct ast_format_def vp8_f = {
+ .name = "VP8",
+ .exts = "vp8",
+ .open = vp8_open,
+ .write = vp8_write,
+ .seek = vp8_seek,
+ .trunc = vp8_trunc,
+ .tell = vp8_tell,
+ .read = vp8_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct vp8_desc),
+};
+
+static int load_module(void)
+{
+ vp8_f.format = ast_format_vp8;
+ if (ast_format_def_register(&vp8_f)) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_def_unregister(vp8_f.name);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Raw VP8 data",
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND
+ );
=====================================
Xopus/include/asterisk/opus.h
=====================================
@@ -0,0 +1,64 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Lorenzo Miniero <lorenzo at meetecho.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Codec opus externals and format attributes
+ *
+ * RFC - https://tools.ietf.org/rfc/rfc7587.txt
+ */
+#ifndef _AST_FORMAT_OPUS_H_
+#define _AST_FORMAT_OPUS_H_
+
+/*! \brief Maximum sampling rate an endpoint is capable of receiving */
+#define CODEC_OPUS_ATTR_MAX_PLAYBACK_RATE "maxplaybackrate"
+/*! \brief An alias for maxplaybackrate (used in older versions) */
+#define CODEC_OPUS_ATTR_MAX_CODED_AUDIO_BANDWIDTH "maxcodedaudiobandwidth"
+/*! \brief Maximum sampling rate an endpoint is capable of sending */
+#define CODEC_OPUS_ATTR_SPROP_MAX_CAPTURE_RATE "sprop-maxcapturerate"
+/*! \brief Maximum duration of packet (in milliseconds) */
+#define CODEC_OPUS_ATTR_MAX_PTIME "maxptime"
+/*! \brief Duration of packet (in milliseconds) */
+#define CODEC_OPUS_ATTR_PTIME "ptime"
+/*! \brief Maximum average received bit rate (in bits per second) */
+#define CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE "maxaveragebitrate"
+/*! \brief Decode stereo (1) vs mono (0) */
+#define CODEC_OPUS_ATTR_STEREO "stereo"
+/*! \brief Likeliness of sender producing stereo (1) vs mono (0) */
+#define CODEC_OPUS_ATTR_SPROP_STEREO "sprop-stereo"
+/*! \brief Decoder prefers a constant (1) vs variable (0) bitrate */
+#define CODEC_OPUS_ATTR_CBR "cbr"
+/*! \brief Use forward error correction (1) or not (0) */
+#define CODEC_OPUS_ATTR_FEC "useinbandfec"
+/*! \brief Use discontinuous transmission (1) or not (0) */
+#define CODEC_OPUS_ATTR_DTX "usedtx"
+/*! \brief Custom data object */
+#define CODEC_OPUS_ATTR_DATA "data"
+
+/*! \brief Default attribute values */
+#define CODEC_OPUS_DEFAULT_SAMPLE_RATE 48000
+#define CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE 48000
+#define CODEC_OPUS_DEFAULT_MAX_PTIME 120
+#define CODEC_OPUS_DEFAULT_PTIME 20
+#define CODEC_OPUS_DEFAULT_BITRATE 510000
+#define CODEC_OPUS_DEFAULT_CBR 0
+#define CODEC_OPUS_DEFAULT_FEC 1
+#define CODEC_OPUS_DEFAULT_DTX 0
+#define CODEC_OPUS_DEFAULT_STEREO 0
+
+#endif /* _AST_FORMAT_OPUS_H */
=====================================
Xopus/res/res_format_attr_opus.c
=====================================
@@ -0,0 +1,371 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Lorenzo Miniero <lorenzo at meetecho.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Opus format attribute interface
+ *
+ * \author Lorenzo Miniero <lorenzo at meetecho.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#if defined(ASTERISK_REGISTER_FILE)
+ASTERISK_REGISTER_FILE()
+#elif defined(ASTERISK_FILE_VERSION)
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")
+#endif
+
+#include "asterisk/module.h"
+#include "asterisk/format.h"
+#include "asterisk/logger.h" /* for ast_log, LOG_WARNING */
+#include "asterisk/opus.h" /* for CODEC_OPUS_DEFAULT_* */
+#include "asterisk/strings.h" /* for ast_str_append */
+#include "asterisk/utils.h" /* for MIN, ast_malloc, ast_free */
+
+/*!
+ * \brief Opus attribute structure.
+ *
+ * \note http://tools.ietf.org/html/rfc7587#section-6
+ */
+struct opus_attr {
+ unsigned int maxbitrate;
+ unsigned int maxplayrate;
+ unsigned int unused; /* was minptime, kept for binary compatibility */
+ unsigned int stereo;
+ unsigned int cbr;
+ unsigned int fec;
+ unsigned int dtx;
+ unsigned int spropmaxcapturerate;
+ unsigned int spropstereo;
+};
+
+static struct opus_attr default_opus_attr = {
+ .maxplayrate = CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE,
+ .spropmaxcapturerate = CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE,
+ .maxbitrate = CODEC_OPUS_DEFAULT_BITRATE,
+ .stereo = CODEC_OPUS_DEFAULT_STEREO,
+ .spropstereo = CODEC_OPUS_DEFAULT_STEREO,
+ .cbr = CODEC_OPUS_DEFAULT_CBR,
+ .fec = CODEC_OPUS_DEFAULT_FEC,
+ .dtx = CODEC_OPUS_DEFAULT_DTX,
+};
+
+static void opus_destroy(struct ast_format *format)
+{
+ struct opus_attr *attr = ast_format_get_attribute_data(format);
+
+ ast_free(attr);
+}
+
+static int opus_clone(const struct ast_format *src, struct ast_format *dst)
+{
+ struct opus_attr *original = ast_format_get_attribute_data(src);
+ struct opus_attr *attr = ast_malloc(sizeof(*attr));
+
+ if (!attr) {
+ return -1;
+ }
+
+ if (original) {
+ *attr = *original;
+ } else {
+ *attr = default_opus_attr;
+ }
+
+ ast_format_set_attribute_data(dst, attr);
+
+ return 0;
+}
+
+static struct ast_format *opus_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
+{
+ struct ast_format *cloned;
+ struct opus_attr *attr;
+ const char *kvp;
+ unsigned int val;
+
+ cloned = ast_format_clone(format);
+ if (!cloned) {
+ return NULL;
+ }
+ attr = ast_format_get_attribute_data(cloned);
+
+ if ((kvp = strstr(attributes, "maxplaybackrate")) && sscanf(kvp, "maxplaybackrate=%30u", &val) == 1) {
+ attr->maxplayrate = val;
+ } else {
+ attr->maxplayrate = 48000;
+ }
+
+ if ((kvp = strstr(attributes, "sprop-maxcapturerate")) && sscanf(kvp, "sprop-maxcapturerate=%30u", &val) == 1) {
+ attr->spropmaxcapturerate = val;
+ } else {
+ attr->spropmaxcapturerate = 48000;
+ }
+
+ if ((kvp = strstr(attributes, "maxaveragebitrate")) && sscanf(kvp, "maxaveragebitrate=%30u", &val) == 1) {
+ attr->maxbitrate = val;
+ } else {
+ attr->maxbitrate = 510000;
+ }
+
+ if (!strncmp(attributes, "stereo=1", 8)) {
+ attr->stereo = 1;
+ } else if (strstr(attributes, " stereo=1")) {
+ attr->stereo = 1;
+ } else if (strstr(attributes, ";stereo=1")) {
+ attr->stereo = 1;
+ } else {
+ attr->stereo = 0;
+ }
+
+ if (strstr(attributes, "sprop-stereo=1")) {
+ attr->spropstereo = 1;
+ } else {
+ attr->spropstereo = 0;
+ }
+
+ if (strstr(attributes, "cbr=1")) {
+ attr->cbr = 1;
+ } else {
+ attr->cbr = 0;
+ }
+
+ if (strstr(attributes, "useinbandfec=1")) {
+ attr->fec = 1;
+ } else {
+ attr->fec = 0;
+ }
+
+ if (strstr(attributes, "usedtx=1")) {
+ attr->dtx = 1;
+ } else {
+ attr->dtx = 0;
+ }
+
+ return cloned;
+}
+
+static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
+{
+ struct opus_attr *attr = ast_format_get_attribute_data(format);
+ int added = 0;
+
+ if (!attr) {
+ /*
+ * (Only) cached formats do not have attribute data assigned because
+ * they were created before this attribute module was registered.
+ * Therefore, we assume the default attribute values here.
+ */
+ attr = &default_opus_attr;
+ }
+
+ if (48000 != attr->maxplayrate) {
+ if (added) {
+ ast_str_append(str, 0, ";");
+ } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+ added = 1;
+ }
+ ast_str_append(str, 0, "maxplaybackrate=%u", attr->maxplayrate);
+ }
+
+ if (48000 != attr->spropmaxcapturerate) {
+ if (added) {
+ ast_str_append(str, 0, ";");
+ } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+ added = 1;
+ }
+ ast_str_append(str, 0, "sprop-maxcapturerate=%u", attr->spropmaxcapturerate);
+ }
+
+ if (510000 != attr->maxbitrate) {
+ if (added) {
+ ast_str_append(str, 0, ";");
+ } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+ added = 1;
+ }
+ ast_str_append(str, 0, "maxaveragebitrate=%u", attr->maxbitrate);
+ }
+
+ if (0 != attr->stereo) {
+ if (added) {
+ ast_str_append(str, 0, ";");
+ } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+ added = 1;
+ }
+ ast_str_append(str, 0, "stereo=%u", attr->stereo);
+ }
+
+ if (0 != attr->spropstereo) {
+ if (added) {
+ ast_str_append(str, 0, ";");
+ } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+ added = 1;
+ }
+ ast_str_append(str, 0, "sprop-stereo=%u", attr->spropstereo);
+ }
+
+ if (0 != attr->cbr) {
+ if (added) {
+ ast_str_append(str, 0, ";");
+ } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+ added = 1;
+ }
+ ast_str_append(str, 0, "cbr=%u", attr->cbr);
+ }
+
+ if (0 != attr->fec) {
+ if (added) {
+ ast_str_append(str, 0, ";");
+ } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+ added = 1;
+ }
+ ast_str_append(str, 0, "useinbandfec=%u", attr->fec);
+ }
+
+ if (0 != attr->dtx) {
+ if (added) {
+ ast_str_append(str, 0, ";");
+ } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+ added = 1;
+ }
+ ast_str_append(str, 0, "usedtx=%u", attr->dtx);
+ }
+
+ if (added) {
+ ast_str_append(str, 0, "\r\n");
+ }
+}
+
+static struct ast_format *opus_getjoint(const struct ast_format *format1, const struct ast_format *format2)
+{
+ struct opus_attr *attr1 = ast_format_get_attribute_data(format1);
+ struct opus_attr *attr2 = ast_format_get_attribute_data(format2);
+ struct ast_format *jointformat;
+ struct opus_attr *attr_res;
+
+ if (!attr1) {
+ attr1 = &default_opus_attr;
+ }
+
+ if (!attr2) {
+ attr2 = &default_opus_attr;
+ }
+
+ jointformat = ast_format_clone(format1);
+ if (!jointformat) {
+ return NULL;
+ }
+ attr_res = ast_format_get_attribute_data(jointformat);
+
+ attr_res->dtx = attr1->dtx || attr2->dtx ? 1 : 0;
+
+ /* Only do FEC if both sides want it. If a peer specifically requests not
+ * to receive with FEC, it may be a waste of bandwidth. */
+ attr_res->fec = attr1->fec && attr2->fec ? 1 : 0;
+
+ attr_res->cbr = attr1->cbr || attr2->cbr ? 1 : 0;
+ attr_res->spropstereo = attr1->spropstereo || attr2->spropstereo ? 1 : 0;
+
+ /* Only do stereo if both sides want it. If a peer specifically requests not
+ * to receive stereo signals, it may be a waste of bandwidth. */
+ attr_res->stereo = attr1->stereo && attr2->stereo ? 1 : 0;
+
+ attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
+ attr_res->spropmaxcapturerate = MIN(attr1->spropmaxcapturerate, attr2->spropmaxcapturerate);
+ attr_res->maxplayrate = MIN(attr1->maxplayrate, attr2->maxplayrate);
+
+ return jointformat;
+}
+
+static struct ast_format *opus_set(const struct ast_format *format, const char *name, const char *value)
+{
+ struct ast_format *cloned;
+ struct opus_attr *attr;
+ unsigned int val;
+
+ if (sscanf(value, "%30u", &val) != 1) {
+ ast_log(LOG_WARNING, "Unknown value '%s' for attribute type '%s'\n",
+ value, name);
+ return NULL;
+ }
+
+ cloned = ast_format_clone(format);
+ if (!cloned) {
+ return NULL;
+ }
+ attr = ast_format_get_attribute_data(cloned);
+
+ if (!strcasecmp(name, "max_bitrate")) {
+ attr->maxbitrate = val;
+ } else if (!strcasecmp(name, "max_playrate")) {
+ attr->maxplayrate = val;
+ } else if (!strcasecmp(name, "minptime")) {
+ attr->unused = val;
+ } else if (!strcasecmp(name, "stereo")) {
+ attr->stereo = val;
+ } else if (!strcasecmp(name, "cbr")) {
+ attr->cbr = val;
+ } else if (!strcasecmp(name, "fec")) {
+ attr->fec = val;
+ } else if (!strcasecmp(name, "dtx")) {
+ attr->dtx = val;
+ } else if (!strcasecmp(name, "sprop_capture_rate")) {
+ attr->spropmaxcapturerate = val;
+ } else if (!strcasecmp(name, "sprop_stereo")) {
+ attr->spropstereo = val;
+ } else {
+ ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
+ }
+
+ return cloned;
+}
+
+static struct ast_format_interface opus_interface = {
+ .format_destroy = opus_destroy,
+ .format_clone = opus_clone,
+ .format_get_joint = opus_getjoint,
+ .format_attribute_set = opus_set,
+ .format_parse_sdp_fmtp = opus_parse_sdp_fmtp,
+ .format_generate_sdp_fmtp = opus_generate_sdp_fmtp,
+};
+
+static int load_module(void)
+{
+ if (ast_format_interface_register("opus", &opus_interface)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Opus Format Attribute Module",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
=====================================
debian/changelog
=====================================
@@ -1,3 +1,21 @@
+asterisk (1:16.23.0~dfsg+~cs6.10.40431411-1) unstable; urgency=medium
+
+ * embed project asterisk-opus as component;
+ add patches 2015 2016 to integrate opus module with asterisk;
+ integrate opus module with build rules;
+ have asterisk-modules replace and break asterisk-opus
+ * update copyright info: update coverage
+ * relax to build-depend unversioned on libjansson-dev:
+ required version satisfied in all supported Debian releases
+ * build-depend on libneon27-dev
+ (not libneon27-gnutls-dev, with libneon27-dev only as fallback);
+ drop fallback build-dependencies
+ for libcurl4-openssl-dev libradcli-dev
+ * build-depend on liblua5.2-dev (not liblua5.1-0-dev)
+ * build-depend on libcodec2-dev libfftw3-dev libsndfile1-dev
+
+ -- Jonas Smedegaard <dr at jones.dk> Fri, 04 Feb 2022 00:40:53 +0100
+
asterisk (1:16.23.0~dfsg+~cs6.10.20220309-2) unstable; urgency=medium
* update debhelper script dh_asterisk:
=====================================
debian/clean
=====================================
@@ -1,10 +1,14 @@
aclocal.m4
addons/mp3/*
codecs/codec_amr.c
+codecs/codec_opus_open_source.c
codecs/ex_amr.h
+codecs/ex_opus.h
debian/build-pjproject/
debian/dh_asterisk.1
doc/core-en_US.xml
+formats/format_ogg_opus_open_source.c
+formats/format_vp8.c
include/asterisk/amr.h
utils/poll.c
menuselect/mxml/mxml.pc
=====================================
debian/control
=====================================
@@ -22,18 +22,20 @@ Build-Depends:
libbluetooth-dev [linux-any],
libc-client2007e-dev,
libcap-dev [linux-any],
- libcurl4-openssl-dev | libcurl-dev,
+ libcodec2-dev,
+ libcurl4-openssl-dev,
libedit-dev,
+ libfftw3-dev,
libgmime-3.0-dev,
libgsm1-dev,
libical-dev,
libiksemel-dev,
libjack-dev,
- libjansson-dev (>= 2.11),
+ libjansson-dev,
libldap-dev,
- liblua5.1-0-dev,
+ liblua5.2-dev,
libncurses-dev,
- libneon27-gnutls-dev | libneon27-dev,
+ libneon27-dev,
libnewt-dev,
libogg-dev,
libopencore-amrnb-dev,
@@ -44,10 +46,11 @@ Build-Depends:
libpopt-dev,
libpq-dev,
libpri-dev,
- libradcli-dev | libfreeradius-client-dev | libradiusclient-ng-dev,
+ libradcli-dev,
libreadline-dev,
libresample1-dev,
libsdl-image1.2-dev,
+ libsndfile1-dev,
libsnmp-dev,
libspandsp-dev,
libspeex-dev,
@@ -130,6 +133,10 @@ Architecture: any
Depends:
${misc:Depends},
${shlibs:Depends},
+Replaces:
+ asterisk-opus (<< 13.7+20171009-2+),
+Breaks:
+ asterisk-opus (<< 13.7+20171009-2+),
Section: libs
Description: loadable modules for the Asterisk PBX
Asterisk is an Open Source PBX and telephony toolkit.
=====================================
debian/copyright
=====================================
@@ -3,6 +3,7 @@ Upstream-Name: Asterisk
Source: https://downloads.asterisk.org/pub/asterisk
https://github.com/traud/asterisk-amr
https://svn.digium.com/svn/thirdparty/mp3/trunk/
+ https://github.com/traud/asterisk-opus
https://github.com/asterisk/pjproject
.
Excluded files:
@@ -175,6 +176,19 @@ Comment:
Version of license not mentioned.
License dictates that to mean any version of the license.
+Files:
+ Xopus/*
+Copyright:
+ 2013,2016 Digium, Inc.
+ 2014 Lorenzo Miniero <lorenzo at meetecho.com>
+License-Grant:
+ This program is free software, distributed under the terms of
+ the GNU General Public License Version 2. See the LICENSE file
+ at the top of the source tree.
+License: GPL-2
+Reference:
+ Xopus/LICENSE
+
Files:
Xpjproject/pjsip-apps/src/pjsua/ios/*
Copyright:
@@ -716,6 +730,16 @@ Copyright:
1995-2002 Brian M. Clapper
License: BSD-4-Clause~Clapper
+Files: codecs/ex_opus.h
+Copyright:
+ 2014 Lorenzo Miniero <lorenzo at meetecho.com>
+License-Grant:
+ Distributed under the terms of the GNU General Public License
+License: GPL-1+
+Comment:
+ Version of license not mentioned.
+ License dictates that to mean any version of the license.
+
Files:
autoconf/ax_pthread.m4
Copyright:
=====================================
debian/copyright_hints
=====================================
@@ -27,6 +27,10 @@ Files: BSDmakefile
Xmp3/mpg123.h
Xmp3/mpglib.h
Xmp3/tabinit.c
+ Xopus/Makefile
+ Xopus/README.md
+ Xopus/asterisk.patch
+ Xopus/enable_native_plc.patch
Xpjproject/INSTALL.txt
Xpjproject/Makefile
Xpjproject/README-RTEMS
@@ -1331,6 +1335,8 @@ Files: BSDmakefile
debian/patches/2012_amr_codec.patch
debian/patches/2013_mp3.patch
debian/patches/2014_pjproject_unbreak.patch
+ debian/patches/2015_opus.patch
+ debian/patches/2016_opus_plc.patch
debian/patches/README
debian/patches/series
debian/rules
@@ -1601,7 +1607,10 @@ Copyright: NONE
License: UNKNOWN
FIXME
-Files: addons/chan_mobile.c
+Files: Xopus/formats/format_ogg_opus_open_source.c
+ Xopus/include/asterisk/opus.h
+ Xopus/res/res_format_attr_opus.c
+ addons/chan_mobile.c
addons/res_config_mysql.c
apps/app_adsiprog.c
apps/app_agent_pool.c
@@ -4167,6 +4176,18 @@ Copyright: 1989, 1991, Free Software Foundation, Inc.
License: GPL-2
FIXME
+Files: Xopus/LICENSE
+ Xpjproject/COPYING
+Copyright: 1989, 1991, Free Software Foundation, Inc.,
+License: GPL-2
+ FIXME
+
+Files: Xopus/codecs/codec_opus_open_source.c
+ Xopus/formats/format_vp8.c
+Copyright: 2014, Lorenzo Miniero
+License: GPL-2
+ FIXME
+
Files: channels/console_video.h
channels/vgrabbers.c
Copyright: 2007, Luigi Rizzo
@@ -4368,6 +4389,11 @@ Copyright: Format: http://www.debian.org/doc/packaging-manuals/copyright-format/
License: GPL
FIXME
+Files: Xopus/codecs/ex_opus.h
+Copyright: 2014, Lorenzo Miniero
+License: GPL
+ FIXME
+
Files: channels/chan_phone.h
Copyright: 1999, Mark Spencer
License: GPL
@@ -4603,11 +4629,6 @@ Copyright: 2010, FIXME
License: GPL-2
FIXME
-Files: Xpjproject/COPYING
-Copyright: 1989, 1991, Free Software Foundation, Inc.,
-License: GPL-2
- FIXME
-
Files: apps/app_directed_pickup.c
Copyright: 2005, Joshua Colp
2008, Gary Cook
=====================================
debian/gbp.conf
=====================================
@@ -10,4 +10,4 @@ sign-tags = True
filter = */.git*
debian-branch = debian/latest
upstream-branch = upstream/latest
-components = [ 'Xamr', 'Xmp3', 'Xpjproject' ]
+components = [ 'Xamr', 'Xmp3', 'Xopus', 'Xpjproject' ]
=====================================
debian/patches/2015_opus.patch
=====================================
@@ -0,0 +1,101 @@
+Description: integrate opus module with asterisk build routines
+ Original patch adapted by Jonas Smedegaard to fit Debian packaging,
+ and to remove original patch file to help detect later upstream changes..
+Author: Sean Bright <sean at callshaper.com>
+Author: Alexander Traud <pabstraud at compuserve.com>
+Author: Jonas Smedegaard <dr at jones.dk>
+Source: https://github.com/traud/asterisk-opus/blob/asterisk-13.7/asterisk.patch
+Last-Update: 2022-01-22
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+--- a/build_tools/menuselect-deps.in
++++ b/build_tools/menuselect-deps.in
+@@ -47,6 +47,7 @@
+ NEON29=@PBX_NEON29@
+ OGG=@PBX_OGG@
+ OPUS=@PBX_OPUS@
++OPUSENC=@PBX_OPUSENC@
+ OPUSFILE=@PBX_OPUSFILE@
+ OSPTK=@PBX_OSPTK@
+ OSS=@PBX_OSS@
+--- a/configure.ac
++++ b/configure.ac
+@@ -571,6 +571,7 @@
+ AST_EXT_LIB_SETUP([OGG], [OGG], [ogg])
+ AST_EXT_LIB_SETUP([OPENR2], [MFR2], [openr2])
+ AST_EXT_LIB_SETUP([OPUS], [Opus], [opus])
++AST_EXT_LIB_SETUP([OPUSENC], [Opusenc], [opusenc])
+ AST_EXT_LIB_SETUP([OPUSFILE], [Opusfile], [opusfile])
+ AST_EXT_LIB_SETUP([OSPTK], [OSP Toolkit], [osptk])
+ AST_EXT_LIB_SETUP([OSS], [Open Sound System], [oss])
+@@ -2611,6 +2612,7 @@
+ if test -z "$__opus_include" -o x"$__opus_include" = x" " ; then
+ __opus_include=-I/usr/include/opus
+ fi
++AST_PKG_CONFIG_CHECK(OPUSENC, libopusenc)
+ AST_EXT_LIB_CHECK([OPUSFILE], [opusfile], [op_open_callbacks], [opus/opusfile.h], [], [$__opus_include])
+
+ for ver in ${LUA_VERSIONS:-5.4 5.3 5.2 5.1}; do
+--- a/makeopts.in
++++ b/makeopts.in
+@@ -228,6 +228,9 @@
+ OPUS_INCLUDE=@OPUS_INCLUDE@
+ OPUS_LIB=@OPUS_LIB@
+
++OPUSENC_INCLUDE=@OPUSENC_INCLUDE@
++OPUSENC_LIB=@OPUSENC_LIB@
++
+ OPUSFILE_INCLUDE=@OPUSFILE_INCLUDE@
+ OPUSFILE_LIB=@OPUSFILE_LIB@
+
+--- a/formats/Makefile
++++ b/formats/Makefile
+@@ -11,6 +11,8 @@
+
+ -include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
++_ASTCFLAGS+=-DASTERISK_VERSION_NUM=${ASTERISKVERSIONNUM}
++
+ MODULE_PREFIX=format
+ MENUSELECT_CATEGORY=FORMATS
+ MENUSELECT_DESCRIPTION=Format Interpreters
+--- a/Xopus/asterisk.patch
++++ /dev/null
+@@ -1,37 +0,0 @@
+---- a/build_tools/menuselect-deps.in
+-+++ b/build_tools/menuselect-deps.in
+-@@ -44,2 +44,3 @@
+- OPUS=@PBX_OPUS@
+-+OPUSENC=@PBX_OPUSENC@
+- OPUSFILE=@PBX_OPUSFILE@
+---- a/configure.ac
+-+++ b/configure.ac
+-@@ -523,2 +523,3 @@
+- AST_EXT_LIB_SETUP([OPUS], [Opus], [opus])
+-+AST_EXT_LIB_SETUP([OPUSENC], [Opusenc], [opusenc])
+- AST_EXT_LIB_SETUP([OPUSFILE], [Opusfile], [opusfile])
+-@@ -2524,2 +2525,3 @@
+- fi
+-+AST_PKG_CONFIG_CHECK(OPUSENC, libopusenc)
+- AST_EXT_LIB_CHECK([OPUSFILE], [opusfile], [op_open_callbacks], [opus/opusfile.h], [], [$__opus_include])
+---- a/makeopts.in
+-+++ b/makeopts.in
+-@@ -228,6 +228,9 @@
+- OPUS_INCLUDE=@OPUS_INCLUDE@
+- OPUS_LIB=@OPUS_LIB@
+-
+-+OPUSENC_INCLUDE=@OPUSENC_INCLUDE@
+-+OPUSENC_LIB=@OPUSENC_LIB@
+-+
+- OPUSFILE_INCLUDE=@OPUSFILE_INCLUDE@
+- OPUSFILE_LIB=@OPUSFILE_LIB@
+-
+---- formats/Makefile
+-+++ formats/Makefile
+-@@ -12,4 +12,6 @@
+- -include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+-
+-+_ASTCFLAGS+=-DASTERISK_VERSION_NUM=${ASTERISKVERSIONNUM}
+-+
+- MODULE_PREFIX=format
+- MENUSELECT_CATEGORY=FORMATS
=====================================
debian/patches/2016_opus_plc.patch
=====================================
@@ -0,0 +1,331 @@
+Description: detect lost (and late) RTP packets for native PLC
+ Original patch adapted by Jonas Smedegaard to fit Debian packaging,
+ and to remove original patch file to help detect later upstream changes..
+Author: Alexander Traud <pabstraud at compuserve.com>
+Author: Jonas Smedegaard <dr at jones.dk>
+Source: https://github.com/traud/asterisk-opus/blob/asterisk-13.7/enable_native_plc.patch
+Last-Update: 2022-01-24
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+--- a/main/translate.c
++++ b/main/translate.c
+@@ -356,6 +356,7 @@
+ pvt->f.offset = AST_FRIENDLY_OFFSET;
+ pvt->f.src = pvt->t->name;
+ pvt->f.data.ptr = pvt->outbuf.c;
++ pvt->f.seqno = 0x10000;
+
+ /*
+ * If the translator has not provided a format
+@@ -564,13 +565,46 @@
+ /*! \brief do the actual translation */
+ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume)
+ {
++ const unsigned int rtp_seqno_max_value = 0xffff;
+ struct ast_trans_pvt *p = path;
+- struct ast_frame *out;
++ struct ast_frame *out_last, *out = NULL;
+ struct timeval delivery;
+ int has_timing_info;
+ long ts;
+ long len;
+- int seqno;
++ int seqno, frames_missing;
++
++ /* Determine the amount of lost packets for PLC */
++ /* But not at start with first frame = path->f.seqno is still 0x10000 */
++ /* But not when there is no sequence number = frame created internally */
++ if ((path->f.seqno <= rtp_seqno_max_value) && (path->f.seqno != f->seqno)) {
++ if (f->seqno < path->f.seqno) { /* seqno overrun situation */
++ frames_missing = rtp_seqno_max_value + f->seqno - path->f.seqno - 1;
++ } else {
++ frames_missing = f->seqno - path->f.seqno - 1;
++ }
++ /* Out-of-order packet - more precise: late packet */
++ if ((rtp_seqno_max_value + 1) / 2 < frames_missing) {
++ if (consume) {
++ ast_frfree(f);
++ }
++ /*
++ * Do not pass late packets to any transcoding module, because that
++ * confuses the state of any library (packets inter-depend). With
++ * the next packet, this one is going to be treated as lost packet.
++ */
++ return NULL;
++ }
++
++ if (frames_missing > 96) {
++ struct ast_str *str = ast_str_alloca(256);
++
++ /* not DEBUG but NOTICE because of WARNING in main/cannel.c:__ast_queue_frame */
++ ast_log(LOG_NOTICE, "%d lost frame(s) %d/%d %s\n", frames_missing, f->seqno, path->f.seqno, ast_translate_path_to_str(path, &str));
++ }
++ } else {
++ frames_missing = 0;
++ }
+
+ if (f->frametype == AST_FRAME_RTCP) {
+ /* Just pass the feedback to the right callback, if it exists.
+@@ -611,19 +645,94 @@
+ f->samples, ast_format_get_sample_rate(f->subclass.format)));
+ }
+ delivery = f->delivery;
+- for (out = f; out && p ; p = p->next) {
+- struct ast_frame *current = out;
+
+- do {
+- framein(p, current);
+- current = AST_LIST_NEXT(current, frame_list);
+- } while (current);
+- if (out != f) {
+- ast_frfree(out);
++ for (out_last = NULL; frames_missing + 1; frames_missing--) {
++ struct ast_frame *frame_to_translate, *inner_head;
++ struct ast_frame missed = {
++ .frametype = AST_FRAME_VOICE,
++ .subclass.format = f->subclass.format,
++ .datalen = 0,
++ /* In RTP, the amount of samples might change anytime */
++ /* If that happened while frames got lost, what to do? */
++ .samples = f->samples, /* FIXME */
++ .src = __FUNCTION__,
++ .data.uint32 = 0,
++ .delivery.tv_sec = 0,
++ .delivery.tv_usec = 0,
++ .flags = 0,
++ /* RTP sequence number is between 0x0001 and 0xffff */
++ .seqno = (rtp_seqno_max_value + 1 + f->seqno - frames_missing) & rtp_seqno_max_value,
++ };
++
++ if (frames_missing) {
++ frame_to_translate = &missed;
++ } else {
++ frame_to_translate = f;
++ }
++
++ /* The translation path from one format to another might contain several steps */
++ /* out* collects the result for missed frame(s) and input frame(s) */
++ /* out is the result of the conversion of all frames, translated into the destination format */
++ /* out_last is the last frame in that list, to add frames faster */
++ for (p = path, inner_head = frame_to_translate; inner_head && p; p = p->next) {
++ struct ast_frame *current, *inner_last, *inner_prev = frame_to_translate;
++
++ /* inner* collects the result of each conversion step, the input for the next step */
++ /* inner_head is a list of frames created by each conversion step */
++ /* inner_last is the last frame in that list, to add frames faster */
++ for (inner_last = NULL, current = inner_head; current; current = AST_LIST_NEXT(current, frame_list)) {
++ struct ast_frame *tmp;
++
++ framein(p, current);
++ tmp = p->t->frameout(p);
++
++ if (!tmp) {
++ continue;
++ } else if (inner_last) {
++ struct ast_frame *t;
++
++ /* Determine the last frame of the list before appending to it */
++ while ((t = AST_LIST_NEXT(inner_last, frame_list))) {
++ inner_last = t;
++ }
++ AST_LIST_NEXT(inner_last, frame_list) = tmp;
++ } else {
++ inner_prev = inner_head;
++ inner_head = tmp;
++ inner_last = tmp;
++ }
++ }
++
++ /* The current step did not create any frames = no frames for the next step */
++ /* The steps are not lost because framein buffered those for the next input frame */
++ if (!inner_last) {
++ inner_prev = inner_head;
++ inner_head = NULL;
++ }
++ if (inner_prev != frame_to_translate) {
++ ast_frfree(inner_prev); /* Frees just the intermediate lists */
++ }
++ }
++
++ /* This frame created no frames after translation = continue with next frame */
++ /* The frame is not lost because framein buffered it to be combined with the next frame */
++ if (!inner_head) {
++ continue;
++ } else if (out_last) {
++ struct ast_frame *t;
++
++ /* Determine the last frame of the list before appending to it */
++ while ((t = AST_LIST_NEXT(out_last, frame_list))) {
++ out_last = t;
++ }
++ AST_LIST_NEXT(out_last, frame_list) = inner_head;
++ } else {
++ out = inner_head;
++ out_last = inner_head;
+ }
+- out = p->t->frameout(p);
+ }
+
++
+ if (!out) {
+ out = generate_interpolated_slin(path, f);
+ }
+--- a/Xopus/enable_native_plc.patch
++++ /dev/null
+@@ -1,156 +0,0 @@
+---- a/main/translate.c (Asterisk 13.17.2)
+-+++ b/main/translate.c (working copy)
+-@@ -359,4 +359,5 @@
+- pvt->f.src = pvt->t->name;
+- pvt->f.data.ptr = pvt->outbuf.c;
+-+ pvt->f.seqno = 0x10000;
+-
+- /*
+-@@ -531,11 +532,44 @@
+- struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume)
+- {
+-+ const unsigned int rtp_seqno_max_value = 0xffff;
+- struct ast_trans_pvt *p = path;
+-- struct ast_frame *out;
+-+ struct ast_frame *out_last, *out = NULL;
+- struct timeval delivery;
+- int has_timing_info;
+- long ts;
+- long len;
+-- int seqno;
+-+ int seqno, frames_missing;
+-+
+-+ /* Determine the amount of lost packets for PLC */
+-+ /* But not at start with first frame = path->f.seqno is still 0x10000 */
+-+ /* But not when there is no sequence number = frame created internally */
+-+ if ((path->f.seqno <= rtp_seqno_max_value) && (path->f.seqno != f->seqno)) {
+-+ if (f->seqno < path->f.seqno) { /* seqno overrun situation */
+-+ frames_missing = rtp_seqno_max_value + f->seqno - path->f.seqno - 1;
+-+ } else {
+-+ frames_missing = f->seqno - path->f.seqno - 1;
+-+ }
+-+ /* Out-of-order packet - more precise: late packet */
+-+ if ((rtp_seqno_max_value + 1) / 2 < frames_missing) {
+-+ if (consume) {
+-+ ast_frfree(f);
+-+ }
+-+ /*
+-+ * Do not pass late packets to any transcoding module, because that
+-+ * confuses the state of any library (packets inter-depend). With
+-+ * the next packet, this one is going to be treated as lost packet.
+-+ */
+-+ return NULL;
+-+ }
+-+
+-+ if (frames_missing > 96) {
+-+ struct ast_str *str = ast_str_alloca(256);
+-+
+-+ /* not DEBUG but NOTICE because of WARNING in main/cannel.c:__ast_queue_frame */
+-+ ast_log(LOG_NOTICE, "%d lost frame(s) %d/%d %s\n", frames_missing, f->seqno, path->f.seqno, ast_translate_path_to_str(path, &str));
+-+ }
+-+ } else {
+-+ frames_missing = 0;
+-+ }
+-
+- has_timing_info = ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO);
+-@@ -567,16 +601,91 @@
+- }
+- delivery = f->delivery;
+-- for (out = f; out && p ; p = p->next) {
+-- struct ast_frame *current = out;
+-
+-- do {
+-- framein(p, current);
+-- current = AST_LIST_NEXT(current, frame_list);
+-- } while (current);
+-- if (out != f) {
+-- ast_frfree(out);
+-+ for (out_last = NULL; frames_missing + 1; frames_missing--) {
+-+ struct ast_frame *frame_to_translate, *inner_head;
+-+ struct ast_frame missed = {
+-+ .frametype = AST_FRAME_VOICE,
+-+ .subclass.format = f->subclass.format,
+-+ .datalen = 0,
+-+ /* In RTP, the amount of samples might change anytime */
+-+ /* If that happened while frames got lost, what to do? */
+-+ .samples = f->samples, /* FIXME */
+-+ .src = __FUNCTION__,
+-+ .data.uint32 = 0,
+-+ .delivery.tv_sec = 0,
+-+ .delivery.tv_usec = 0,
+-+ .flags = 0,
+-+ /* RTP sequence number is between 0x0001 and 0xffff */
+-+ .seqno = (rtp_seqno_max_value + 1 + f->seqno - frames_missing) & rtp_seqno_max_value,
+-+ };
+-+
+-+ if (frames_missing) {
+-+ frame_to_translate = &missed;
+-+ } else {
+-+ frame_to_translate = f;
+-+ }
+-+
+-+ /* The translation path from one format to another might contain several steps */
+-+ /* out* collects the result for missed frame(s) and input frame(s) */
+-+ /* out is the result of the conversion of all frames, translated into the destination format */
+-+ /* out_last is the last frame in that list, to add frames faster */
+-+ for (p = path, inner_head = frame_to_translate; inner_head && p; p = p->next) {
+-+ struct ast_frame *current, *inner_last, *inner_prev = frame_to_translate;
+-+
+-+ /* inner* collects the result of each conversion step, the input for the next step */
+-+ /* inner_head is a list of frames created by each conversion step */
+-+ /* inner_last is the last frame in that list, to add frames faster */
+-+ for (inner_last = NULL, current = inner_head; current; current = AST_LIST_NEXT(current, frame_list)) {
+-+ struct ast_frame *tmp;
+-+
+-+ framein(p, current);
+-+ tmp = p->t->frameout(p);
+-+
+-+ if (!tmp) {
+-+ continue;
+-+ } else if (inner_last) {
+-+ struct ast_frame *t;
+-+
+-+ /* Determine the last frame of the list before appending to it */
+-+ while ((t = AST_LIST_NEXT(inner_last, frame_list))) {
+-+ inner_last = t;
+-+ }
+-+ AST_LIST_NEXT(inner_last, frame_list) = tmp;
+-+ } else {
+-+ inner_prev = inner_head;
+-+ inner_head = tmp;
+-+ inner_last = tmp;
+-+ }
+-+ }
+-+
+-+ /* The current step did not create any frames = no frames for the next step */
+-+ /* The steps are not lost because framein buffered those for the next input frame */
+-+ if (!inner_last) {
+-+ inner_prev = inner_head;
+-+ inner_head = NULL;
+-+ }
+-+ if (inner_prev != frame_to_translate) {
+-+ ast_frfree(inner_prev); /* Frees just the intermediate lists */
+-+ }
+-+ }
+-+
+-+ /* This frame created no frames after translation = continue with next frame */
+-+ /* The frame is not lost because framein buffered it to be combined with the next frame */
+-+ if (!inner_head) {
+-+ continue;
+-+ } else if (out_last) {
+-+ struct ast_frame *t;
+-+
+-+ /* Determine the last frame of the list before appending to it */
+-+ while ((t = AST_LIST_NEXT(out_last, frame_list))) {
+-+ out_last = t;
+-+ }
+-+ AST_LIST_NEXT(out_last, frame_list) = inner_head;
+-+ } else {
+-+ out = inner_head;
+-+ out_last = inner_head;
+- }
+-- out = p->t->frameout(p);
+- }
+-+
+- if (out) {
+- /* we have a frame, play with times */
=====================================
debian/patches/series
=====================================
@@ -43,3 +43,5 @@
2012_amr_codec.patch
2013_mp3.patch
2014_pjproject_unbreak.patch
+2015_opus.patch
+2016_opus_plc.patch
=====================================
debian/rules
=====================================
@@ -83,6 +83,8 @@ execute_before_dh_auto_configure:
mkdir addons/mp3
cp --force --target-directory=addons/mp3 Xmp3/*
+ cp --force --recursive --target-directory=. Xopus/codecs Xopus/formats
+
override_dh_auto_configure:
TMPDIR="$(CURDIR)/$(PJDIR)" $(FETCH_ENV) dh_auto_configure -- \
--disable-asteriskssl \
=====================================
debian/watch
=====================================
@@ -24,6 +24,13 @@ dversionmangle=auto \
https://svn.digium.com/svn/thirdparty/mp3/trunk/ \
HEAD checksum
+opts=\
+component=Xopus,\
+mode=git,pgpmode=none,pretty=0.0.%cd,\
+dversionmangle=auto \
+https://github.com/traud/asterisk-opus.git \
+HEAD checksum
+
opts=\
component=Xpjproject,\
filenamemangle=s/.*?(@ANY_VERSION@@ARCHIVE_EXT@)/@PACKAGE at -Xpjproject-$1/,\
View it on GitLab: https://salsa.debian.org/pkg-voip-team/asterisk/-/compare/1b0f4346db03e9e4d795a40ad6c38798de1c4781...75ccadcca6879756558ed3d0b73bedf3a6a55a68
--
View it on GitLab: https://salsa.debian.org/pkg-voip-team/asterisk/-/compare/1b0f4346db03e9e4d795a40ad6c38798de1c4781...75ccadcca6879756558ed3d0b73bedf3a6a55a68
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-voip-maintainers/attachments/20220204/2d30b1ba/attachment-0001.htm>
More information about the Pkg-voip-maintainers
mailing list