├── LICENSE ├── Makefile ├── README.md ├── asterisk.patch ├── codecs ├── codec_opus_open_source.c └── ex_opus.h ├── enable_native_plc.patch ├── formats ├── format_ogg_opus_open_source.c └── format_vp8.c ├── include └── asterisk │ └── opus.h └── res └── res_format_attr_opus.c /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | prefix=/usr/local 2 | exec_prefix=$(prefix) 3 | libdir=$(exec_prefix)/lib 4 | 5 | # build with `make OPUSENC=0` to disable rewrite support using libopusenc 6 | OPUSENC?=1 7 | 8 | CFLAGS=-pthread -D_FORTIFY_SOURCE=2 -fPIC 9 | DEBUG=-g3 10 | OPTIMIZE=-O3 11 | CPPFLAGS= 12 | DEFS= 13 | INSTALL=/usr/bin/install -c 14 | LDFLAGS=-pthread -Wl,--warn-common 15 | LIBS= 16 | SHELL=/bin/sh 17 | 18 | ASTMODDIR=$(libdir)/asterisk/modules 19 | MODULES=codec_opus_open_source format_ogg_opus_open_source format_vp8 res_format_attr_opus 20 | 21 | .SUFFIXES: .c .so 22 | 23 | .PHONY: all clean install uninstall $(MODULES) 24 | 25 | all: $(MODULES) 26 | 27 | clean: 28 | rm -f */*.so 29 | 30 | install: $(MODULES) 31 | $(INSTALL) -D -t $(DESTDIR)$(ASTMODDIR) */*.so 32 | 33 | uninstall: 34 | cd $(ASTMODDIR) && rm -f $(addsuffix .so,$(MODULES)) 35 | 36 | codec_opus_open_source: LIBS+=-lopus 37 | codec_opus_open_source: DEFS+=-DAST_MODULE=\"codec_opus_open_source\" \ 38 | -DAST_MODULE_SELF_SYM=__internal_codec_opus_open_source_self 39 | codec_opus_open_source: codecs/codec_opus_open_source.so 40 | 41 | format_ogg_opus_open_source: CPATH+=-I/usr/include/opus 42 | format_ogg_opus_open_source: LIBS+=-lopus -lopusfile 43 | format_ogg_opus_open_source: DEFS+=-DAST_MODULE=\"format_ogg_opus_open_source\" \ 44 | -DAST_MODULE_SELF_SYM=__internal_format_ogg_opus_open_source_self 45 | ifeq ($(OPUSENC),1) 46 | format_ogg_opus_open_source: LIBS+=-lopusenc 47 | format_ogg_opus_open_source: DEFS+=-DHAVE_OPUSENC 48 | endif 49 | format_ogg_opus_open_source: formats/format_ogg_opus_open_source.so 50 | 51 | format_vp8: DEFS+=-DAST_MODULE=\"format_vp8\" \ 52 | -DAST_MODULE_SELF_SYM=__internal_format_vp8_self 53 | format_vp8: formats/format_vp8.so 54 | 55 | res_format_attr_opus: DEFS+=-DAST_MODULE=\"res_format_attr_opus\" \ 56 | -DAST_MODULE_SELF_SYM=__internal_res_format_attr_opus_self 57 | res_format_attr_opus: res/res_format_attr_opus.so 58 | 59 | .c.so: 60 | $(CC) -o $@ $(CPATH) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(DEBUG) $(OPTIMIZE) $(LIBS) -shared $(LDFLAGS) $< 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Opus Codec for Asterisk 2 | 3 | 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. 4 | 5 | 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 6 | 7 | A. enabling `codec_opus` via `make menuselect`, or 8 | 9 | B. downloading the module from [Digium Downloads](http://www.digium.com/products/asterisk/downloads) » Add-on Voice Codecs, or 10 | 11 | C. downloading the module [directly](http://downloads.digium.com/pub/telephony/codec_opus/). 12 | 13 | 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. 14 | 15 | 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. 16 | 17 | ## Installing 18 | 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/)! 19 | 20 | cd /usr/src/ 21 | wget downloads.asterisk.org/pub/telephony/asterisk/asterisk-13-current.tar.gz 22 | tar zxf ./asterisk* 23 | cd ./asterisk* 24 | 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 25 | 26 | Install libraries: 27 | 28 | To support transcoding, you’ll need to install an Opus library, for example in Debian/Ubuntu: 29 | 30 | sudo apt-get --assume-yes install libopusfile-dev 31 | 32 | Apply all changes: 33 | 34 | wget github.com/traud/asterisk-opus/archive/asterisk-13.7.tar.gz 35 | tar zxf ./master.tar.gz 36 | rm ./master.tar.gz 37 | cp --verbose ./asterisk-opus*/include/asterisk/* ./include/asterisk 38 | cp --verbose ./asterisk-opus*/codecs/* ./codecs 39 | cp --verbose ./asterisk-opus*/res/* ./res 40 | 41 | (Optionally) apply the patch for File Formats (untested): 42 | 43 | Two format modules are added which allow you to play VP8 and Ogg Opus files without transcoding. 44 | 45 | cp --verbose ./asterisk-opus*/formats/* ./formats 46 | patch -p1 <./asterisk-opus*/asterisk.patch 47 | 48 | (Optionally) apply the patch for Native PLC (experimental): 49 | 50 | 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) 51 | 52 | patch -p1 <./asterisk-opus*/enable_native_plc.patch 53 | 54 | Run the bootstrap script to re-generate configure: 55 | 56 | ./bootstrap.sh 57 | 58 | Configure your patched Asterisk: 59 | 60 | ./configure 61 | 62 | Enable slin16 in menuselect for transcoding, for example via: 63 | 64 | make menuselect.makeopts 65 | ./menuselect/menuselect --enable-category MENUSELECT_CORE_SOUNDS 66 | 67 | Compile and install: 68 | 69 | make 70 | sudo make install 71 | 72 | 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. 73 | 74 | ## Testing 75 | 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. 76 | 77 | 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. 78 | 79 | 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). 80 | 81 | ## What is missing 82 | * `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`. 83 | * 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. 84 | * 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. 85 | 86 | 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/). 87 | 88 | 89 | ## Thanks go to 90 | * [Opus team](http://www.opus-codec.org/contact/), 91 | * Ron Lee packaging the libraries for Debian/Ubuntu, 92 | * [Lorenzo Miniero](https://github.com/meetecho/asterisk-opus) created the original code for Asterisk 11, 93 | * [Tzafrir Cohen](http://issues.asterisk.org/jira/browse/ASTERISK-21981) drove the pass-through support for Asterisk 13, 94 | * [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. -------------------------------------------------------------------------------- /asterisk.patch: -------------------------------------------------------------------------------- 1 | --- a/build_tools/menuselect-deps.in 2 | +++ b/build_tools/menuselect-deps.in 3 | @@ -44,2 +44,3 @@ 4 | OPUS=@PBX_OPUS@ 5 | +OPUSENC=@PBX_OPUSENC@ 6 | OPUSFILE=@PBX_OPUSFILE@ 7 | --- a/configure.ac 8 | +++ b/configure.ac 9 | @@ -523,2 +523,3 @@ 10 | AST_EXT_LIB_SETUP([OPUS], [Opus], [opus]) 11 | +AST_EXT_LIB_SETUP([OPUSENC], [Opusenc], [opusenc]) 12 | AST_EXT_LIB_SETUP([OPUSFILE], [Opusfile], [opusfile]) 13 | @@ -2524,2 +2525,3 @@ 14 | fi 15 | +AST_PKG_CONFIG_CHECK(OPUSENC, libopusenc) 16 | AST_EXT_LIB_CHECK([OPUSFILE], [opusfile], [op_open_callbacks], [opus/opusfile.h], [], [$__opus_include]) 17 | --- a/makeopts.in 18 | +++ b/makeopts.in 19 | @@ -228,6 +228,9 @@ 20 | OPUS_INCLUDE=@OPUS_INCLUDE@ 21 | OPUS_LIB=@OPUS_LIB@ 22 | 23 | +OPUSENC_INCLUDE=@OPUSENC_INCLUDE@ 24 | +OPUSENC_LIB=@OPUSENC_LIB@ 25 | + 26 | OPUSFILE_INCLUDE=@OPUSFILE_INCLUDE@ 27 | OPUSFILE_LIB=@OPUSFILE_LIB@ 28 | 29 | --- formats/Makefile 30 | +++ formats/Makefile 31 | @@ -12,4 +12,6 @@ 32 | -include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps 33 | 34 | +_ASTCFLAGS+=-DASTERISK_VERSION_NUM=${ASTERISKVERSIONNUM} 35 | + 36 | MODULE_PREFIX=format 37 | MENUSELECT_CATEGORY=FORMATS 38 | -------------------------------------------------------------------------------- /codecs/codec_opus_open_source.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2014, Lorenzo Miniero 5 | * 6 | * Lorenzo Miniero 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | /*! \file 20 | * 21 | * \brief Translate between signed linear and Opus (Open Codec) 22 | * 23 | * \author Lorenzo Miniero 24 | * 25 | * \note This work was motivated by Mozilla 26 | * 27 | * \ingroup codecs 28 | * 29 | * \extref http://www.opus-codec.org/docs/html_api-1.1.0/ 30 | * 31 | */ 32 | 33 | /*** MODULEINFO 34 | opus 35 | codec_opus 36 | yes 37 | ***/ 38 | 39 | #include "asterisk.h" 40 | 41 | #if defined(ASTERISK_REGISTER_FILE) 42 | ASTERISK_REGISTER_FILE() 43 | #elif defined(ASTERISK_FILE_VERSION) 44 | ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") 45 | #endif 46 | 47 | #include "asterisk/astobj2.h" /* for ao2_ref */ 48 | #include "asterisk/cli.h" /* for ast_cli_entry, ast_cli, etc */ 49 | #include "asterisk/codec.h" /* for ast_codec_get */ 50 | #include "asterisk/format.h" /* for ast_format_get_attribute_data */ 51 | #include "asterisk/frame.h" /* for ast_frame, etc */ 52 | #include "asterisk/linkedlists.h" /* for AST_LIST_NEXT, etc */ 53 | #include "asterisk/lock.h" /* for ast_atomic_fetchadd_int */ 54 | #include "asterisk/logger.h" /* for ast_log, LOG_ERROR, etc */ 55 | #include "asterisk/module.h" 56 | #include "asterisk/translate.h" /* for ast_trans_pvt, etc */ 57 | #include "asterisk/utils.h" /* for ARRAY_LEN */ 58 | 59 | #include 60 | 61 | #include "asterisk/opus.h" /* for CODEC_OPUS_DEFAULT_* */ 62 | 63 | #define BUFFER_SAMPLES 5760 64 | #define MAX_CHANNELS 2 65 | #define OPUS_SAMPLES 960 66 | 67 | /* Sample frame data */ 68 | #include "asterisk/slin.h" 69 | #include "ex_opus.h" 70 | 71 | static struct codec_usage { 72 | int encoder_id; 73 | int decoder_id; 74 | int encoders; 75 | int decoders; 76 | } usage; 77 | 78 | /* 79 | * Stores the function pointer 'sample_count' of the cached ast_codec 80 | * before this module was loaded. Allows to restore this previous 81 | * function pointer, when this module in unloaded. 82 | */ 83 | static struct ast_codec *opus_codec; /* codec of the cached format */ 84 | static int (*opus_samples_previous)(struct ast_frame *frame); 85 | 86 | /* Private structures */ 87 | struct opus_coder_pvt { 88 | void *opus; /* May be encoder or decoder */ 89 | int sampling_rate; 90 | int multiplier; 91 | int id; 92 | int16_t buf[BUFFER_SAMPLES]; 93 | int framesize; 94 | int inited; 95 | int channels; 96 | int decode_fec_incoming; 97 | int previous_lost; 98 | }; 99 | 100 | struct opus_attr { 101 | unsigned int maxbitrate; 102 | unsigned int maxplayrate; 103 | unsigned int unused; /* was minptime */ 104 | unsigned int stereo; 105 | unsigned int cbr; 106 | unsigned int fec; 107 | unsigned int dtx; 108 | unsigned int spropmaxcapturerate; /* FIXME: not utilised, yet */ 109 | unsigned int spropstereo; /* FIXME: currently, we are just mono */ 110 | }; 111 | 112 | /* Helper methods */ 113 | static int opus_encoder_construct(struct ast_trans_pvt *pvt, int sampling_rate) 114 | { 115 | struct opus_coder_pvt *opvt = pvt->pvt; 116 | struct opus_attr *attr = pvt->explicit_dst ? ast_format_get_attribute_data(pvt->explicit_dst) : NULL; 117 | const opus_int32 bitrate = attr ? attr->maxbitrate : CODEC_OPUS_DEFAULT_BITRATE; 118 | const int maxplayrate = attr ? attr->maxplayrate : CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE; 119 | const int channels = attr ? attr->stereo + 1 : CODEC_OPUS_DEFAULT_STEREO + 1; 120 | const opus_int32 vbr = attr ? !(attr->cbr) : !CODEC_OPUS_DEFAULT_CBR; 121 | const opus_int32 fec = attr ? attr->fec : CODEC_OPUS_DEFAULT_FEC; 122 | const opus_int32 dtx = attr ? attr->dtx : CODEC_OPUS_DEFAULT_DTX; 123 | const int application = OPUS_APPLICATION_VOIP; 124 | int status = 0; 125 | 126 | opvt->opus = opus_encoder_create(sampling_rate, channels, application, &status); 127 | 128 | if (status != OPUS_OK) { 129 | ast_log(LOG_ERROR, "Error creating the Opus encoder: %s\n", opus_strerror(status)); 130 | return -1; 131 | } 132 | 133 | if (sampling_rate <= 8000 || maxplayrate <= 8000) { 134 | status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); 135 | } else if (sampling_rate <= 12000 || maxplayrate <= 12000) { 136 | status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND)); 137 | } else if (sampling_rate <= 16000 || maxplayrate <= 16000) { 138 | status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND)); 139 | } else if (sampling_rate <= 24000 || maxplayrate <= 24000) { 140 | status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND)); 141 | } /* else we use the default: OPUS_BANDWIDTH_FULLBAND */ 142 | 143 | if (0 < bitrate && bitrate != 510000) { 144 | status = opus_encoder_ctl(opvt->opus, OPUS_SET_BITRATE(bitrate)); 145 | } /* else we use the default: OPUS_AUTO */ 146 | status = opus_encoder_ctl(opvt->opus, OPUS_SET_VBR(vbr)); 147 | status = opus_encoder_ctl(opvt->opus, OPUS_SET_INBAND_FEC(fec)); 148 | status = opus_encoder_ctl(opvt->opus, OPUS_SET_DTX(dtx)); 149 | 150 | opvt->sampling_rate = sampling_rate; 151 | opvt->multiplier = 48000 / sampling_rate; 152 | opvt->framesize = sampling_rate / 50; 153 | opvt->id = ast_atomic_fetchadd_int(&usage.encoder_id, 1) + 1; 154 | 155 | ast_atomic_fetchadd_int(&usage.encoders, +1); 156 | 157 | ast_debug(3, "Created encoder #%d (%d -> opus)\n", opvt->id, sampling_rate); 158 | 159 | return 0; 160 | } 161 | 162 | static int opus_decoder_construct(struct ast_trans_pvt *pvt, struct ast_frame *f) 163 | { 164 | struct opus_coder_pvt *opvt = pvt->pvt; 165 | /* struct opus_attr *attr = ast_format_get_attribute_data(f->subclass.format); */ 166 | int error = 0; 167 | 168 | opvt->sampling_rate = pvt->t->dst_codec.sample_rate; 169 | opvt->multiplier = 48000 / opvt->sampling_rate; 170 | opvt->channels = /* attr ? attr->spropstereo + 1 :*/ 1; /* FIXME */ 171 | 172 | opvt->opus = opus_decoder_create(opvt->sampling_rate, opvt->channels, &error); 173 | 174 | if (error != OPUS_OK) { 175 | ast_log(LOG_ERROR, "Error creating the Opus decoder: %s\n", opus_strerror(error)); 176 | return -1; 177 | } 178 | 179 | opvt->id = ast_atomic_fetchadd_int(&usage.decoder_id, 1) + 1; 180 | 181 | ast_atomic_fetchadd_int(&usage.decoders, +1); 182 | 183 | ast_debug(3, "Created decoder #%d (opus -> %d)\n", opvt->id, opvt->sampling_rate); 184 | 185 | return 0; 186 | } 187 | 188 | /* Translator callbacks */ 189 | static int lintoopus_new(struct ast_trans_pvt *pvt) 190 | { 191 | return opus_encoder_construct(pvt, pvt->t->src_codec.sample_rate); 192 | } 193 | 194 | static int opustolin_new(struct ast_trans_pvt *pvt) 195 | { 196 | struct opus_coder_pvt *opvt = pvt->pvt; 197 | 198 | opvt->previous_lost = 0; /* we are new and have not lost anything */ 199 | opvt->inited = 0; /* we do not know the "sprop" values, yet */ 200 | 201 | return 0; 202 | } 203 | 204 | static int lintoopus_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) 205 | { 206 | struct opus_coder_pvt *opvt = pvt->pvt; 207 | 208 | /* XXX We should look at how old the rest of our stream is, and if it 209 | is too old, then we should overwrite it entirely, otherwise we can 210 | get artifacts of earlier talk that do not belong */ 211 | memcpy(opvt->buf + pvt->samples, f->data.ptr, f->datalen); 212 | pvt->samples += f->samples; 213 | 214 | return 0; 215 | } 216 | 217 | static struct ast_frame *lintoopus_frameout(struct ast_trans_pvt *pvt) 218 | { 219 | struct opus_coder_pvt *opvt = pvt->pvt; 220 | struct ast_frame *result = NULL; 221 | struct ast_frame *last = NULL; 222 | int samples = 0; /* output samples */ 223 | 224 | while (pvt->samples >= opvt->framesize) { 225 | /* status is either error or output bytes */ 226 | const int status = opus_encode(opvt->opus, 227 | opvt->buf + samples, 228 | opvt->framesize, 229 | pvt->outbuf.uc, 230 | BUFFER_SAMPLES); 231 | 232 | samples += opvt->framesize; 233 | pvt->samples -= opvt->framesize; 234 | 235 | if (status < 0) { 236 | ast_log(LOG_ERROR, "Error encoding the Opus frame: %s\n", opus_strerror(status)); 237 | } else { 238 | struct ast_frame *current = ast_trans_frameout(pvt, 239 | status, 240 | OPUS_SAMPLES); 241 | 242 | if (!current) { 243 | continue; 244 | } else if (last) { 245 | AST_LIST_NEXT(last, frame_list) = current; 246 | } else { 247 | result = current; 248 | } 249 | last = current; 250 | } 251 | } 252 | 253 | /* Move the data at the end of the buffer to the front */ 254 | if (samples) { 255 | memmove(opvt->buf, opvt->buf + samples, pvt->samples * 2); 256 | } 257 | 258 | return result; 259 | } 260 | 261 | static int opustolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) 262 | { 263 | struct opus_coder_pvt *opvt = pvt->pvt; 264 | int decode_fec; 265 | int frame_size; 266 | opus_int16 *dst; 267 | opus_int32 len; 268 | unsigned char *src; 269 | int status; 270 | 271 | if (!opvt->inited && f->datalen == 0) { 272 | return 0; /* we cannot start without data */ 273 | } else if (!opvt->inited) { /* 0 < f->datalen */ 274 | status = opus_decoder_construct(pvt, f); 275 | opvt->inited = 1; 276 | if (status) { 277 | return status; 278 | } 279 | } 280 | 281 | /* 282 | * When we get a frame indicator (ast_null_frame), format is NULL. Because FEC 283 | * status can change any time (SDP re-negotiation), we save again and again. 284 | */ 285 | if (f->subclass.format) { 286 | struct opus_attr *attr = ast_format_get_attribute_data(f->subclass.format); 287 | 288 | if (attr) { 289 | opvt->decode_fec_incoming = attr->fec; 290 | } 291 | } 292 | decode_fec = opvt->decode_fec_incoming; 293 | 294 | /* 295 | * The Opus Codec, actually its library allows 296 | * - Forward-Error Correction (FEC), and 297 | * - native Packet-Loss Concealment (PLC). 298 | * The sender might include FEC. If there is no FEC, because it was not send 299 | * or the FEC data got lost, the API of the Opus library does PLC instead. 300 | * Therefore we have three boolean variables: 301 | * - current frame got lost: f->datalen == 0, 302 | * - previous frame got lost: opvt->previous_lost, and 303 | * - FEC negotiated on SDP layer: decode_fec. 304 | * Now, we go through all cases. Because some cases use the same source code 305 | * we have less than 8 (2^3) cases. 306 | * 307 | * Some notes on the coding style of this section: 308 | * This code section is passed for each incoming frame, normally every 309 | * 20 milliseconds. For each channel, this code is passed individually. 310 | * Therefore, this code should be as performant as possible. On the other 311 | * hand, PLC plus FEC is complicated. Therefore, code readability is one 312 | * prerequisite to understand, debug, and review this code section. Because 313 | * we do have optimising compilers, we are able to sacrify optimised code 314 | * for code readability. If you find an error or unnecessary calculation 315 | * which is not optimised = removed by your compiler, please, create an 316 | * issue on . I am just 317 | * a human and human do mistakes. However, humans love to learn. 318 | * 319 | * Source-code examples are 320 | * - , 321 | * - 322 | * and the official mailing list itself: 323 | * . 324 | */ 325 | 326 | /* Case 1 and 2 */ 327 | if (f->datalen == 0 && opvt->previous_lost) { 328 | /* 329 | * If this frame and the previous frame got lost, we do not have any 330 | * data for FEC. Therefore, we go for PLC on the previous frame. However, 331 | * the next frame could include FEC for the currently lost frame. 332 | * Therefore, we "wait" for the next frame to fix the current frame. 333 | */ 334 | decode_fec = 0; /* = do PLC */ 335 | opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size)); 336 | dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); 337 | len = 0; 338 | src = NULL; 339 | status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec); 340 | if (status < 0) { 341 | ast_log(LOG_ERROR, "%s\n", opus_strerror(status)); 342 | } else { 343 | pvt->samples += status; 344 | pvt->datalen += status * opvt->channels * sizeof(int16_t); 345 | } 346 | /* 347 | * Save the state of the current frame, whether it is lost = "wait". 348 | * That way, we are able to decide whether to do FEC next time. 349 | */ 350 | opvt->previous_lost = (f->datalen == 0 || status < 0); 351 | return 0; 352 | } 353 | 354 | /* Case 3 */ 355 | if (f->datalen == 0 && !decode_fec) { /* !opvt->previous_lost */ 356 | /* 357 | * The sender stated in SDP: "I am not going to provide FEC". Therefore, 358 | * we do not wait for the next frame and do PLC right away. 359 | */ 360 | decode_fec = 0; 361 | opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size)); 362 | dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); 363 | len = f->datalen; 364 | src = NULL; 365 | status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec); 366 | if (status < 0) { 367 | ast_log(LOG_ERROR, "%s\n", opus_strerror(status)); 368 | } else { 369 | pvt->samples += status; 370 | pvt->datalen += status * opvt->channels * sizeof(int16_t); 371 | } 372 | opvt->previous_lost = (f->datalen == 0 || status < 0); 373 | return 0; 374 | } 375 | 376 | /* Case 4 */ 377 | if (f->datalen == 0) { /* decode_fec && !opvt->previous_lost */ 378 | /* 379 | * The previous frame was of no issue. Therefore, we do not have to 380 | * reconstruct it. We do not have any data in the current frame but the 381 | * sender might give us FEC with the next frame. We cannot do anything 382 | * but wait for the next frame. Till Asterisk 13.7, this creates the 383 | * warning "opustolin48 did not update samples 0". Please, ignore this 384 | * warning or apply the patch included in the GitHub repository. 385 | */ 386 | status = 0; /* no samples to add currently */ 387 | if (status < 0) { 388 | ast_log(LOG_ERROR, "%s\n", opus_strerror(status)); 389 | } else { 390 | pvt->samples += status; 391 | pvt->datalen += status * opvt->channels * sizeof(int16_t); 392 | } 393 | opvt->previous_lost = (f->datalen == 0 || status < 0); 394 | return 0; 395 | } 396 | 397 | /* Case 5 and 6 */ 398 | if (!opvt->previous_lost) { /* 0 < f->datalen */ 399 | /* 400 | * The perfect case - the previous frame was not lost and we have data 401 | * in the current frame. Therefore, neither FEC nor PLC are required. 402 | */ 403 | decode_fec = 0; 404 | frame_size = BUFFER_SAMPLES / opvt->multiplier; /* parse everything */ 405 | dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); 406 | len = f->datalen; 407 | src = f->data.ptr; 408 | status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec); 409 | if (status < 0) { 410 | ast_log(LOG_ERROR, "%s\n", opus_strerror(status)); 411 | } else { 412 | pvt->samples += status; 413 | pvt->datalen += status * opvt->channels * sizeof(int16_t); 414 | } 415 | opvt->previous_lost = (f->datalen == 0 || status < 0); 416 | return 0; 417 | } 418 | 419 | /* Case 7 */ 420 | if (!decode_fec) { /* 0 < f->datalen && opvt->previous_lost */ 421 | /* 422 | * The previous frame got lost and the sender stated in SDP: "I am not 423 | * going to provide FEC". Therefore, we do PLC. Furthermore, we try to 424 | * decode the current frame because we have data. This creates jitter 425 | * because we create double the amount of frames as normal, see 426 | * . If this is 427 | * an issue for your use-case, please, file and issue report on 428 | * . 429 | */ 430 | decode_fec = 0; 431 | opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size)); 432 | dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); 433 | len = 0; 434 | src = NULL; 435 | status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec); 436 | if (status < 0) { 437 | ast_log(LOG_ERROR, "%s\n", opus_strerror(status)); 438 | } else { 439 | pvt->samples += status; 440 | pvt->datalen += status * opvt->channels * sizeof(int16_t); 441 | } 442 | decode_fec = 0; 443 | frame_size = BUFFER_SAMPLES / opvt->multiplier; /* parse everything */ 444 | dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); /* append after PLC data */ 445 | len = f->datalen; 446 | src = f->data.ptr; 447 | status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec); 448 | if (status < 0) { 449 | ast_log(LOG_ERROR, "%s\n", opus_strerror(status)); 450 | } else { 451 | pvt->samples += status; 452 | pvt->datalen += status * opvt->channels * sizeof(int16_t); 453 | } 454 | opvt->previous_lost = (f->datalen == 0 || status < 0); 455 | return 0; 456 | } 457 | 458 | /* Case 8; Last Case */ 459 | { /* 0 < f->datalen && opvt->previous_lost && decode_fec */ 460 | decode_fec = 1; 461 | opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size)); 462 | dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); 463 | len = f->datalen; 464 | src = f->data.ptr; 465 | status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec); 466 | if (status < 0) { 467 | ast_log(LOG_ERROR, "%s\n", opus_strerror(status)); 468 | } else { 469 | pvt->samples += status; 470 | pvt->datalen += status * opvt->channels * sizeof(int16_t); 471 | } 472 | decode_fec = 0; 473 | frame_size = BUFFER_SAMPLES / opvt->multiplier; /* parse everything */ 474 | dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); /* append after FEC data */ 475 | len = f->datalen; 476 | src = f->data.ptr; 477 | status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec); 478 | if (status < 0) { 479 | ast_log(LOG_ERROR, "%s\n", opus_strerror(status)); 480 | } else { 481 | pvt->samples += status; 482 | pvt->datalen += status * opvt->channels * sizeof(int16_t); 483 | } 484 | opvt->previous_lost = (f->datalen == 0 || status < 0); 485 | return 0; 486 | } 487 | } 488 | 489 | static void lintoopus_destroy(struct ast_trans_pvt *arg) 490 | { 491 | struct opus_coder_pvt *opvt = arg->pvt; 492 | 493 | if (!opvt || !opvt->opus) { 494 | return; 495 | } 496 | 497 | opus_encoder_destroy(opvt->opus); 498 | opvt->opus = NULL; 499 | 500 | ast_atomic_fetchadd_int(&usage.encoders, -1); 501 | 502 | ast_debug(3, "Destroyed encoder #%d (%d->opus)\n", opvt->id, opvt->sampling_rate); 503 | } 504 | 505 | static void opustolin_destroy(struct ast_trans_pvt *arg) 506 | { 507 | struct opus_coder_pvt *opvt = arg->pvt; 508 | 509 | if (!opvt || !opvt->opus) { 510 | return; 511 | } 512 | 513 | opus_decoder_destroy(opvt->opus); 514 | opvt->opus = NULL; 515 | 516 | ast_atomic_fetchadd_int(&usage.decoders, -1); 517 | 518 | ast_debug(3, "Destroyed decoder #%d (opus->%d)\n", opvt->id, opvt->sampling_rate); 519 | } 520 | 521 | static char *handle_cli_opus_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 522 | { 523 | struct codec_usage copy; 524 | 525 | switch (cmd) { 526 | case CLI_INIT: 527 | e->command = "opus show"; 528 | e->usage = 529 | "Usage: opus show\n" 530 | " Displays Opus encoder/decoder utilization.\n"; 531 | return NULL; 532 | case CLI_GENERATE: 533 | return NULL; 534 | } 535 | 536 | if (a->argc != 2) { 537 | return CLI_SHOWUSAGE; 538 | } 539 | 540 | copy = usage; 541 | 542 | ast_cli(a->fd, "%d/%d encoders/decoders are in use.\n", copy.encoders, copy.decoders); 543 | 544 | return CLI_SUCCESS; 545 | } 546 | 547 | /* Translators */ 548 | static struct ast_translator opustolin = { 549 | .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP, 550 | .name = "opustolin", 551 | .src_codec = { 552 | .name = "opus", 553 | .type = AST_MEDIA_TYPE_AUDIO, 554 | .sample_rate = 48000, 555 | }, 556 | .dst_codec = { 557 | .name = "slin", 558 | .type = AST_MEDIA_TYPE_AUDIO, 559 | .sample_rate = 8000, 560 | }, 561 | .format = "slin", 562 | .newpvt = opustolin_new, 563 | .framein = opustolin_framein, 564 | .destroy = opustolin_destroy, 565 | .sample = opus_sample, 566 | .desc_size = sizeof(struct opus_coder_pvt), 567 | .buffer_samples = (BUFFER_SAMPLES / (48000 / 8000)) * 2, /* because of possible FEC */ 568 | .buf_size = (BUFFER_SAMPLES / (48000 / 8000)) * MAX_CHANNELS * sizeof(opus_int16) * 2, 569 | .native_plc = 1, 570 | }; 571 | 572 | static struct ast_translator lintoopus = { 573 | .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP, 574 | .name = "lintoopus", 575 | .src_codec = { 576 | .name = "slin", 577 | .type = AST_MEDIA_TYPE_AUDIO, 578 | .sample_rate = 8000, 579 | }, 580 | .dst_codec = { 581 | .name = "opus", 582 | .type = AST_MEDIA_TYPE_AUDIO, 583 | .sample_rate = 48000, 584 | }, 585 | .format = "opus", 586 | .newpvt = lintoopus_new, 587 | .framein = lintoopus_framein, 588 | .frameout = lintoopus_frameout, 589 | .destroy = lintoopus_destroy, 590 | .sample = slin8_sample, 591 | .desc_size = sizeof(struct opus_coder_pvt), 592 | .buffer_samples = BUFFER_SAMPLES, 593 | .buf_size = BUFFER_SAMPLES * 2, 594 | }; 595 | 596 | static struct ast_translator opustolin12 = { 597 | .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 1, 598 | .name = "opustolin12", 599 | .src_codec = { 600 | .name = "opus", 601 | .type = AST_MEDIA_TYPE_AUDIO, 602 | .sample_rate = 48000, 603 | }, 604 | .dst_codec = { 605 | .name = "slin", 606 | .type = AST_MEDIA_TYPE_AUDIO, 607 | .sample_rate = 12000, 608 | }, 609 | .format = "slin12", 610 | .newpvt = opustolin_new, 611 | .framein = opustolin_framein, 612 | .destroy = opustolin_destroy, 613 | .sample = opus_sample, 614 | .desc_size = sizeof(struct opus_coder_pvt), 615 | .buffer_samples = (BUFFER_SAMPLES / (48000 / 12000)) * 2, /* because of possible FEC */ 616 | .buf_size = (BUFFER_SAMPLES / (48000 / 12000)) * MAX_CHANNELS * sizeof(opus_int16) * 2, 617 | .native_plc = 1, 618 | }; 619 | 620 | static struct ast_translator lin12toopus = { 621 | .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 1, 622 | .name = "lin12toopus", 623 | .src_codec = { 624 | .name = "slin", 625 | .type = AST_MEDIA_TYPE_AUDIO, 626 | .sample_rate = 12000, 627 | }, 628 | .dst_codec = { 629 | .name = "opus", 630 | .type = AST_MEDIA_TYPE_AUDIO, 631 | .sample_rate = 48000, 632 | }, 633 | .format = "opus", 634 | .newpvt = lintoopus_new, 635 | .framein = lintoopus_framein, 636 | .frameout = lintoopus_frameout, 637 | .destroy = lintoopus_destroy, 638 | .desc_size = sizeof(struct opus_coder_pvt), 639 | .buffer_samples = BUFFER_SAMPLES, 640 | .buf_size = BUFFER_SAMPLES * 2, 641 | }; 642 | 643 | static struct ast_translator opustolin16 = { 644 | .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 2, 645 | .name = "opustolin16", 646 | .src_codec = { 647 | .name = "opus", 648 | .type = AST_MEDIA_TYPE_AUDIO, 649 | .sample_rate = 48000, 650 | }, 651 | .dst_codec = { 652 | .name = "slin", 653 | .type = AST_MEDIA_TYPE_AUDIO, 654 | .sample_rate = 16000, 655 | }, 656 | .format = "slin16", 657 | .newpvt = opustolin_new, 658 | .framein = opustolin_framein, 659 | .destroy = opustolin_destroy, 660 | .sample = opus_sample, 661 | .desc_size = sizeof(struct opus_coder_pvt), 662 | .buffer_samples = (BUFFER_SAMPLES / (48000 / 16000)) * 2, /* because of possible FEC */ 663 | .buf_size = (BUFFER_SAMPLES / (48000 / 16000)) * MAX_CHANNELS * sizeof(opus_int16) * 2, 664 | .native_plc = 1, 665 | }; 666 | 667 | static struct ast_translator lin16toopus = { 668 | .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 2, 669 | .name = "lin16toopus", 670 | .src_codec = { 671 | .name = "slin", 672 | .type = AST_MEDIA_TYPE_AUDIO, 673 | .sample_rate = 16000, 674 | }, 675 | .dst_codec = { 676 | .name = "opus", 677 | .type = AST_MEDIA_TYPE_AUDIO, 678 | .sample_rate = 48000, 679 | }, 680 | .format = "opus", 681 | .newpvt = lintoopus_new, 682 | .framein = lintoopus_framein, 683 | .frameout = lintoopus_frameout, 684 | .destroy = lintoopus_destroy, 685 | .sample = slin16_sample, 686 | .desc_size = sizeof(struct opus_coder_pvt), 687 | .buffer_samples = BUFFER_SAMPLES, 688 | .buf_size = BUFFER_SAMPLES * 2, 689 | }; 690 | 691 | static struct ast_translator opustolin24 = { 692 | .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 4, 693 | .name = "opustolin24", 694 | .src_codec = { 695 | .name = "opus", 696 | .type = AST_MEDIA_TYPE_AUDIO, 697 | .sample_rate = 48000, 698 | }, 699 | .dst_codec = { 700 | .name = "slin", 701 | .type = AST_MEDIA_TYPE_AUDIO, 702 | .sample_rate = 24000, 703 | }, 704 | .format = "slin24", 705 | .newpvt = opustolin_new, 706 | .framein = opustolin_framein, 707 | .destroy = opustolin_destroy, 708 | .sample = opus_sample, 709 | .desc_size = sizeof(struct opus_coder_pvt), 710 | .buffer_samples = (BUFFER_SAMPLES / (48000 / 24000)) * 2, /* because of possible FEC */ 711 | .buf_size = (BUFFER_SAMPLES / (48000 / 24000)) * MAX_CHANNELS * sizeof(opus_int16) * 2, 712 | .native_plc = 1, 713 | }; 714 | 715 | static struct ast_translator lin24toopus = { 716 | .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 4, 717 | .name = "lin24toopus", 718 | .src_codec = { 719 | .name = "slin", 720 | .type = AST_MEDIA_TYPE_AUDIO, 721 | .sample_rate = 24000, 722 | }, 723 | .dst_codec = { 724 | .name = "opus", 725 | .type = AST_MEDIA_TYPE_AUDIO, 726 | .sample_rate = 48000, 727 | }, 728 | .format = "opus", 729 | .newpvt = lintoopus_new, 730 | .framein = lintoopus_framein, 731 | .frameout = lintoopus_frameout, 732 | .destroy = lintoopus_destroy, 733 | .desc_size = sizeof(struct opus_coder_pvt), 734 | .buffer_samples = BUFFER_SAMPLES, 735 | .buf_size = BUFFER_SAMPLES * 2, 736 | }; 737 | 738 | static struct ast_translator opustolin48 = { 739 | .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 8, 740 | .name = "opustolin48", 741 | .src_codec = { 742 | .name = "opus", 743 | .type = AST_MEDIA_TYPE_AUDIO, 744 | .sample_rate = 48000, 745 | }, 746 | .dst_codec = { 747 | .name = "slin", 748 | .type = AST_MEDIA_TYPE_AUDIO, 749 | .sample_rate = 48000, 750 | }, 751 | .format = "slin48", 752 | .newpvt = opustolin_new, 753 | .framein = opustolin_framein, 754 | .destroy = opustolin_destroy, 755 | .sample = opus_sample, 756 | .desc_size = sizeof(struct opus_coder_pvt), 757 | .buffer_samples = BUFFER_SAMPLES * 2, /* twice, because of possible FEC */ 758 | .buf_size = BUFFER_SAMPLES * MAX_CHANNELS * sizeof(opus_int16) * 2, 759 | .native_plc = 1, 760 | }; 761 | 762 | static struct ast_translator lin48toopus = { 763 | .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 8, 764 | .name = "lin48toopus", 765 | .src_codec = { 766 | .name = "slin", 767 | .type = AST_MEDIA_TYPE_AUDIO, 768 | .sample_rate = 48000, 769 | }, 770 | .dst_codec = { 771 | .name = "opus", 772 | .type = AST_MEDIA_TYPE_AUDIO, 773 | .sample_rate = 48000, 774 | }, 775 | .format = "opus", 776 | .newpvt = lintoopus_new, 777 | .framein = lintoopus_framein, 778 | .frameout = lintoopus_frameout, 779 | .destroy = lintoopus_destroy, 780 | .desc_size = sizeof(struct opus_coder_pvt), 781 | .buffer_samples = BUFFER_SAMPLES, 782 | .buf_size = BUFFER_SAMPLES * 2, 783 | }; 784 | 785 | static struct ast_cli_entry cli[] = { 786 | AST_CLI_DEFINE(handle_cli_opus_show, "Display Opus codec utilization.") 787 | }; 788 | 789 | static int opus_samples(struct ast_frame *frame) 790 | { 791 | opus_int32 sampling_rate = 48000; /* FIXME */ 792 | 793 | return opus_packet_get_nb_samples(frame->data.ptr, frame->datalen, sampling_rate); 794 | } 795 | 796 | static int reload(void) 797 | { 798 | /* Reload does nothing */ 799 | return AST_MODULE_LOAD_SUCCESS; 800 | } 801 | 802 | static int unload_module(void) 803 | { 804 | int res; 805 | 806 | opus_codec->samples_count = opus_samples_previous; 807 | ao2_ref(opus_codec, -1); 808 | 809 | res = ast_unregister_translator(&opustolin); 810 | res |= ast_unregister_translator(&lintoopus); 811 | res |= ast_unregister_translator(&opustolin12); 812 | res |= ast_unregister_translator(&lin12toopus); 813 | res |= ast_unregister_translator(&opustolin16); 814 | res |= ast_unregister_translator(&lin16toopus); 815 | res |= ast_unregister_translator(&opustolin24); 816 | res |= ast_unregister_translator(&lin24toopus); 817 | res |= ast_unregister_translator(&opustolin48); 818 | res |= ast_unregister_translator(&lin48toopus); 819 | 820 | ast_cli_unregister_multiple(cli, ARRAY_LEN(cli)); 821 | 822 | return res; 823 | } 824 | 825 | static int load_module(void) 826 | { 827 | int res; 828 | 829 | opus_codec = ast_codec_get("opus", AST_MEDIA_TYPE_AUDIO, 48000); 830 | opus_samples_previous = opus_codec->samples_count; 831 | opus_codec->samples_count = opus_samples; 832 | 833 | res = ast_register_translator(&opustolin); 834 | res |= ast_register_translator(&lintoopus); 835 | res |= ast_register_translator(&opustolin12); 836 | res |= ast_register_translator(&lin12toopus); 837 | res |= ast_register_translator(&opustolin16); 838 | res |= ast_register_translator(&lin16toopus); 839 | res |= ast_register_translator(&opustolin24); 840 | res |= ast_register_translator(&lin24toopus); 841 | res |= ast_register_translator(&opustolin48); 842 | res |= ast_register_translator(&lin48toopus); 843 | 844 | ast_cli_register_multiple(cli, ARRAY_LEN(cli)); 845 | 846 | return res; 847 | } 848 | 849 | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Opus Coder/Decoder", 850 | .load = load_module, 851 | .unload = unload_module, 852 | .reload = reload, 853 | ); 854 | -------------------------------------------------------------------------------- /codecs/ex_opus.h: -------------------------------------------------------------------------------- 1 | /*! \file 2 | * \brief 8-bit data 3 | * 4 | * Copyright (C) 2014, Lorenzo Miniero 5 | * 6 | * Distributed under the terms of the GNU General Public License 7 | * 8 | */ 9 | 10 | #include "asterisk/format_cache.h" /* for ast_format_opus */ 11 | #include "asterisk/frame.h" /* for ast_frame, etc */ 12 | 13 | /* Opus, a 20ms sample */ 14 | static uint8_t ex_opus[] = { 15 | 0x4b, 0x41, 0x25, 0x0b, 0xe4, 0x55, 0xc6, 0x74, 16 | 0xda, 0xbb, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 17 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 18 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 19 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 21 | }; 22 | 23 | static struct ast_frame *opus_sample(void) 24 | { 25 | static struct ast_frame f = { 26 | .frametype = AST_FRAME_VOICE, 27 | .datalen = sizeof(ex_opus), 28 | .samples = OPUS_SAMPLES, 29 | .mallocd = 0, 30 | .offset = 0, 31 | .src = __PRETTY_FUNCTION__, 32 | .data.ptr = ex_opus, 33 | }; 34 | 35 | f.subclass.format = ast_format_opus; 36 | 37 | return &f; 38 | } 39 | -------------------------------------------------------------------------------- /enable_native_plc.patch: -------------------------------------------------------------------------------- 1 | --- a/main/translate.c (Asterisk 13.17.2) 2 | +++ b/main/translate.c (working copy) 3 | @@ -359,4 +359,5 @@ 4 | pvt->f.src = pvt->t->name; 5 | pvt->f.data.ptr = pvt->outbuf.c; 6 | + pvt->f.seqno = 0x10000; 7 | 8 | /* 9 | @@ -531,11 +532,44 @@ 10 | struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume) 11 | { 12 | + const unsigned int rtp_seqno_max_value = 0xffff; 13 | struct ast_trans_pvt *p = path; 14 | - struct ast_frame *out; 15 | + struct ast_frame *out_last, *out = NULL; 16 | struct timeval delivery; 17 | int has_timing_info; 18 | long ts; 19 | long len; 20 | - int seqno; 21 | + int seqno, frames_missing; 22 | + 23 | + /* Determine the amount of lost packets for PLC */ 24 | + /* But not at start with first frame = path->f.seqno is still 0x10000 */ 25 | + /* But not when there is no sequence number = frame created internally */ 26 | + if ((path->f.seqno <= rtp_seqno_max_value) && (path->f.seqno != f->seqno)) { 27 | + if (f->seqno < path->f.seqno) { /* seqno overrun situation */ 28 | + frames_missing = rtp_seqno_max_value + f->seqno - path->f.seqno - 1; 29 | + } else { 30 | + frames_missing = f->seqno - path->f.seqno - 1; 31 | + } 32 | + /* Out-of-order packet - more precise: late packet */ 33 | + if ((rtp_seqno_max_value + 1) / 2 < frames_missing) { 34 | + if (consume) { 35 | + ast_frfree(f); 36 | + } 37 | + /* 38 | + * Do not pass late packets to any transcoding module, because that 39 | + * confuses the state of any library (packets inter-depend). With 40 | + * the next packet, this one is going to be treated as lost packet. 41 | + */ 42 | + return NULL; 43 | + } 44 | + 45 | + if (frames_missing > 96) { 46 | + struct ast_str *str = ast_str_alloca(256); 47 | + 48 | + /* not DEBUG but NOTICE because of WARNING in main/cannel.c:__ast_queue_frame */ 49 | + 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)); 50 | + } 51 | + } else { 52 | + frames_missing = 0; 53 | + } 54 | 55 | has_timing_info = ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO); 56 | @@ -567,16 +601,91 @@ 57 | } 58 | delivery = f->delivery; 59 | - for (out = f; out && p ; p = p->next) { 60 | - struct ast_frame *current = out; 61 | 62 | - do { 63 | - framein(p, current); 64 | - current = AST_LIST_NEXT(current, frame_list); 65 | - } while (current); 66 | - if (out != f) { 67 | - ast_frfree(out); 68 | + for (out_last = NULL; frames_missing + 1; frames_missing--) { 69 | + struct ast_frame *frame_to_translate, *inner_head; 70 | + struct ast_frame missed = { 71 | + .frametype = AST_FRAME_VOICE, 72 | + .subclass.format = f->subclass.format, 73 | + .datalen = 0, 74 | + /* In RTP, the amount of samples might change anytime */ 75 | + /* If that happened while frames got lost, what to do? */ 76 | + .samples = f->samples, /* FIXME */ 77 | + .src = __FUNCTION__, 78 | + .data.uint32 = 0, 79 | + .delivery.tv_sec = 0, 80 | + .delivery.tv_usec = 0, 81 | + .flags = 0, 82 | + /* RTP sequence number is between 0x0001 and 0xffff */ 83 | + .seqno = (rtp_seqno_max_value + 1 + f->seqno - frames_missing) & rtp_seqno_max_value, 84 | + }; 85 | + 86 | + if (frames_missing) { 87 | + frame_to_translate = &missed; 88 | + } else { 89 | + frame_to_translate = f; 90 | + } 91 | + 92 | + /* The translation path from one format to another might contain several steps */ 93 | + /* out* collects the result for missed frame(s) and input frame(s) */ 94 | + /* out is the result of the conversion of all frames, translated into the destination format */ 95 | + /* out_last is the last frame in that list, to add frames faster */ 96 | + for (p = path, inner_head = frame_to_translate; inner_head && p; p = p->next) { 97 | + struct ast_frame *current, *inner_last, *inner_prev = frame_to_translate; 98 | + 99 | + /* inner* collects the result of each conversion step, the input for the next step */ 100 | + /* inner_head is a list of frames created by each conversion step */ 101 | + /* inner_last is the last frame in that list, to add frames faster */ 102 | + for (inner_last = NULL, current = inner_head; current; current = AST_LIST_NEXT(current, frame_list)) { 103 | + struct ast_frame *tmp; 104 | + 105 | + framein(p, current); 106 | + tmp = p->t->frameout(p); 107 | + 108 | + if (!tmp) { 109 | + continue; 110 | + } else if (inner_last) { 111 | + struct ast_frame *t; 112 | + 113 | + /* Determine the last frame of the list before appending to it */ 114 | + while ((t = AST_LIST_NEXT(inner_last, frame_list))) { 115 | + inner_last = t; 116 | + } 117 | + AST_LIST_NEXT(inner_last, frame_list) = tmp; 118 | + } else { 119 | + inner_prev = inner_head; 120 | + inner_head = tmp; 121 | + inner_last = tmp; 122 | + } 123 | + } 124 | + 125 | + /* The current step did not create any frames = no frames for the next step */ 126 | + /* The steps are not lost because framein buffered those for the next input frame */ 127 | + if (!inner_last) { 128 | + inner_prev = inner_head; 129 | + inner_head = NULL; 130 | + } 131 | + if (inner_prev != frame_to_translate) { 132 | + ast_frfree(inner_prev); /* Frees just the intermediate lists */ 133 | + } 134 | + } 135 | + 136 | + /* This frame created no frames after translation = continue with next frame */ 137 | + /* The frame is not lost because framein buffered it to be combined with the next frame */ 138 | + if (!inner_head) { 139 | + continue; 140 | + } else if (out_last) { 141 | + struct ast_frame *t; 142 | + 143 | + /* Determine the last frame of the list before appending to it */ 144 | + while ((t = AST_LIST_NEXT(out_last, frame_list))) { 145 | + out_last = t; 146 | + } 147 | + AST_LIST_NEXT(out_last, frame_list) = inner_head; 148 | + } else { 149 | + out = inner_head; 150 | + out_last = inner_head; 151 | } 152 | - out = p->t->frameout(p); 153 | } 154 | + 155 | if (out) { 156 | /* we have a frame, play with times */ 157 | -------------------------------------------------------------------------------- /formats/format_ogg_opus_open_source.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2016, Digium, Inc. 5 | * 6 | * Mark Michelson 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | /*** MODULEINFO 20 | opusfile 21 | opusenc 22 | format_ogg_opus 23 | yes 24 | ***/ 25 | 26 | #include "asterisk.h" 27 | 28 | #if defined(ASTERISK_REGISTER_FILE) 29 | ASTERISK_REGISTER_FILE() 30 | #elif defined(ASTERISK_FILE_VERSION) 31 | ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") 32 | #endif 33 | 34 | #include /* for SEEK_CUR, SEEK_END, SEEK_SET */ 35 | 36 | #include "asterisk/format_cache.h" /* for ast_format_slin48 */ 37 | #include "asterisk/frame.h" /* for ast_frame, AST_FRIENDLY_OFFSET */ 38 | #include "asterisk/logger.h" /* for ast_log, LOG_ERROR, AST_LOG_ERROR */ 39 | #include "asterisk/mod_format.h" /* for ast_filestream, ast_format_def */ 40 | #include "asterisk/module.h" 41 | #if defined(HAVE_OPUSENC) 42 | #include "asterisk/config.h" /* for ast_variable, ast_config_destroy */ 43 | #include "asterisk/format.h" /* for ast_format_get_... */ 44 | #include "asterisk/utils.h" /* for ast_flags */ 45 | #endif 46 | 47 | #include 48 | #include 49 | #if defined(HAVE_OPUSENC) 50 | #include 51 | #endif 52 | 53 | #include "asterisk/opus.h" 54 | 55 | /* 120ms of 48KHz audio */ 56 | #define SAMPLES_MAX 5760 57 | #define BUF_SIZE (2 * SAMPLES_MAX) 58 | 59 | #if defined(HAVE_OPUSENC) 60 | /* Variables that can be set in formats.conf */ 61 | static int complexity = 10; /* OPUS default */ 62 | static int maxbitrate = CODEC_OPUS_DEFAULT_BITRATE; 63 | #endif 64 | 65 | struct ogg_opus_desc { 66 | OggOpusFile *of; 67 | 68 | #if defined(HAVE_OPUSENC) 69 | OggOpusEnc *enc; 70 | OggOpusComments *comments; 71 | #endif 72 | 73 | size_t writing; 74 | off_t writing_pcm_pos; 75 | }; 76 | 77 | static int fread_wrapper(void *_stream, unsigned char *_ptr, int _nbytes) 78 | { 79 | FILE *stream = _stream; 80 | size_t bytes_read; 81 | 82 | if (!stream || _nbytes < 0) { 83 | return -1; 84 | } 85 | 86 | bytes_read = fread(_ptr, 1, _nbytes, stream); 87 | 88 | return bytes_read > 0 || feof(stream) ? (int) bytes_read : OP_EREAD; 89 | } 90 | 91 | static int fseek_wrapper(void *_stream, opus_int64 _offset, int _whence) 92 | { 93 | FILE *stream = _stream; 94 | 95 | return fseeko(stream, (off_t) _offset, _whence); 96 | } 97 | 98 | static opus_int64 ftell_wrapper(void *_stream) 99 | { 100 | FILE *stream = _stream; 101 | 102 | return ftello(stream); 103 | } 104 | 105 | static int ogg_opus_open(struct ast_filestream *s) 106 | { 107 | struct ogg_opus_desc *desc = (struct ogg_opus_desc *) s->_private; 108 | OpusFileCallbacks cb = { 109 | .read = fread_wrapper, 110 | .seek = fseek_wrapper, 111 | .tell = ftell_wrapper, 112 | .close = NULL, 113 | }; 114 | 115 | memset(desc, 0, sizeof(*desc)); 116 | desc->of = op_open_callbacks(s->f, &cb, NULL, 0, NULL); 117 | if (!desc->of) { 118 | return -1; 119 | } 120 | 121 | return 0; 122 | } 123 | 124 | #if defined(HAVE_OPUSENC) 125 | static int fwrite_wrapper(void *user_data, const unsigned char *ptr, opus_int32 len) 126 | { 127 | FILE *stream = user_data; 128 | 129 | return fwrite(ptr, 1, len, stream) != (size_t) len; 130 | } 131 | 132 | static int fclose_wrapper(void *user_data) 133 | { 134 | return 0; 135 | } 136 | 137 | static const OpusEncCallbacks enc_callbacks = { 138 | .write = fwrite_wrapper, 139 | .close = fclose_wrapper, 140 | }; 141 | 142 | static int ogg_opus_rewrite(struct ast_filestream *fs, const char *comment) 143 | { 144 | struct ogg_opus_desc *desc = fs->_private; 145 | int err, rate, channels, family; 146 | 147 | desc->writing = 1; 148 | desc->writing_pcm_pos = 0; 149 | 150 | desc->comments = ope_comments_create(); 151 | ope_comments_add(desc->comments, "ENCODER", "Asterisk PBX"); 152 | if (comment) 153 | ope_comments_add(desc->comments, "COMMENT", comment); 154 | 155 | rate = ast_format_get_sample_rate(fs->fmt->format); 156 | #if defined(ASTERISK_VERSION_NUM) && (ASTERISK_VERSION_NUM < 150000) 157 | channels = 1; 158 | #else 159 | channels = ast_format_get_channel_count(fs->fmt->format); 160 | #endif 161 | if (channels < 3) { 162 | family = 0; 163 | } else { 164 | family = 1; 165 | } 166 | 167 | desc->enc = ope_encoder_create_callbacks(&enc_callbacks, fs->f, desc->comments, rate, channels, family, &err); 168 | 169 | if (!desc->enc) { 170 | ast_log(AST_LOG_ERROR, "Error creating the OGG/Opus encoder: %s\n", ope_strerror(err)); 171 | return -1; 172 | } 173 | 174 | ope_encoder_ctl(desc->enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); 175 | ope_encoder_ctl(desc->enc, OPUS_SET_COMPLEXITY(complexity)); 176 | ope_encoder_ctl(desc->enc, OPUS_SET_BITRATE(maxbitrate)); 177 | 178 | return 0; 179 | } 180 | 181 | static int ogg_opus_write(struct ast_filestream *fs, struct ast_frame *f) 182 | { 183 | struct ogg_opus_desc *desc = fs->_private; 184 | int err; 185 | 186 | if (!desc->writing) { 187 | ast_log(LOG_ERROR, "This OGG/Opus stream is not set up for writing!\n"); 188 | return -1; 189 | } 190 | 191 | if (!f->datalen) { 192 | return -1; 193 | } 194 | 195 | err = ope_encoder_write(desc->enc, f->data.ptr, f->samples); 196 | if (err) { 197 | ast_log(AST_LOG_ERROR, "Error encoding OGG/Opus frame: %s\n", ope_strerror(err)); 198 | return -1; 199 | } 200 | 201 | desc->writing_pcm_pos += f->samples; 202 | return 0; 203 | } 204 | #else 205 | static int ogg_opus_rewrite(struct ast_filestream *fs, const char *comment) 206 | { 207 | ast_log(LOG_ERROR, "Writing OGG/Opus streams is not built-in\n"); 208 | return -1; 209 | } 210 | 211 | static int ogg_opus_write(struct ast_filestream *fs, struct ast_frame *f) 212 | { 213 | ast_log(LOG_ERROR, "Writing OGG/Opus streams is not built-in\n"); 214 | return -1; 215 | } 216 | #endif 217 | 218 | static int ogg_opus_seek(struct ast_filestream *fs, off_t sample_offset, int whence) 219 | { 220 | int seek_result = -1; 221 | off_t relative_pcm_pos; 222 | struct ogg_opus_desc *desc = fs->_private; 223 | 224 | if (desc->writing) { 225 | return -1; 226 | } 227 | 228 | switch (whence) { 229 | case SEEK_SET: 230 | seek_result = op_pcm_seek(desc->of, sample_offset); 231 | break; 232 | case SEEK_CUR: 233 | if ((relative_pcm_pos = op_pcm_tell(desc->of)) < 0) { 234 | seek_result = -1; 235 | break; 236 | } 237 | seek_result = op_pcm_seek(desc->of, relative_pcm_pos + sample_offset); 238 | break; 239 | case SEEK_END: 240 | if ((relative_pcm_pos = op_pcm_total(desc->of, -1)) < 0) { 241 | seek_result = -1; 242 | break; 243 | } 244 | seek_result = op_pcm_seek(desc->of, relative_pcm_pos - sample_offset); 245 | break; 246 | default: 247 | ast_log(LOG_WARNING, "Unknown *whence* to seek on OGG/Opus streams!\n"); 248 | break; 249 | } 250 | 251 | /* normalize error value to -1,0 */ 252 | return (seek_result == 0) ? 0 : -1; 253 | } 254 | 255 | static int ogg_opus_trunc(struct ast_filestream *fs) 256 | { 257 | return -1; 258 | } 259 | 260 | static off_t ogg_opus_tell(struct ast_filestream *fs) 261 | { 262 | struct ogg_opus_desc *desc = fs->_private; 263 | off_t pos; 264 | 265 | if (desc->writing) { 266 | return desc->writing_pcm_pos / CODEC_OPUS_DEFAULT_SAMPLE_RATE * DEFAULT_SAMPLE_RATE; 267 | } 268 | 269 | pos = (off_t) op_pcm_tell(desc->of); 270 | if (pos < 0) { 271 | return -1; 272 | } 273 | return pos; 274 | } 275 | 276 | static struct ast_frame *ogg_opus_read(struct ast_filestream *fs, int *whennext) 277 | { 278 | struct ogg_opus_desc *desc = fs->_private; 279 | int hole = 1; 280 | int samples_read; 281 | opus_int16 *out_buf; 282 | 283 | if (desc->writing) { 284 | ast_log(LOG_WARNING, "Reading is not supported on OGG/Opus in writing mode.\n"); 285 | return NULL; 286 | } 287 | 288 | AST_FRAME_SET_BUFFER(&fs->fr, fs->buf, AST_FRIENDLY_OFFSET, BUF_SIZE) 289 | 290 | out_buf = (opus_int16 *) fs->fr.data.ptr; 291 | 292 | while (hole) { 293 | samples_read = op_read( 294 | desc->of, 295 | out_buf, 296 | SAMPLES_MAX, 297 | NULL); 298 | 299 | if (samples_read != OP_HOLE) { 300 | hole = 0; 301 | } 302 | } 303 | 304 | if (samples_read <= 0) { 305 | return NULL; 306 | } 307 | 308 | fs->fr.datalen = samples_read * 2; 309 | fs->fr.samples = samples_read; 310 | *whennext = fs->fr.samples; 311 | 312 | return &fs->fr; 313 | } 314 | 315 | static void ogg_opus_close(struct ast_filestream *fs) 316 | { 317 | struct ogg_opus_desc *desc = fs->_private; 318 | 319 | if (desc->writing) { 320 | #if defined(HAVE_OPUSENC) 321 | ope_encoder_drain(desc->enc); 322 | ope_encoder_destroy(desc->enc); 323 | ope_comments_destroy(desc->comments); 324 | #endif 325 | } else { 326 | op_free(desc->of); 327 | } 328 | } 329 | 330 | static struct ast_format_def opus_f = { 331 | .name = "ogg_opus", 332 | .exts = "opus", 333 | .open = ogg_opus_open, 334 | .rewrite = ogg_opus_rewrite, 335 | .write = ogg_opus_write, 336 | .seek = ogg_opus_seek, 337 | .trunc = ogg_opus_trunc, 338 | .tell = ogg_opus_tell, 339 | .read = ogg_opus_read, 340 | .close = ogg_opus_close, 341 | .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, 342 | .desc_size = sizeof(struct ogg_opus_desc), 343 | }; 344 | 345 | static int parse_config(int reload) 346 | { 347 | #if defined(HAVE_OPUSENC) 348 | struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 349 | struct ast_config *cfg = ast_config_load("formats.conf", config_flags); 350 | struct ast_variable *var; 351 | int i, res = 0; 352 | 353 | if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) { 354 | return res; 355 | } 356 | 357 | for (var = ast_variable_browse(cfg, "opus"); var; var = var->next) { 358 | if (!strcasecmp(var->name, "complexity")) { 359 | i = atoi(var->value); 360 | if (i < 0 || i > 10) { 361 | res = 1; 362 | ast_log(LOG_ERROR, "Complexity must be in 0-10\n"); 363 | break; 364 | } 365 | 366 | complexity = i; 367 | } else if (!strcasecmp(var->name, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE)) { 368 | i = atoi(var->value); 369 | if (i < 500 || i > 512000) { 370 | res = 1; 371 | ast_log(LOG_ERROR, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE " must be in 500-512000\n"); 372 | break; 373 | } 374 | 375 | maxbitrate = i; 376 | } 377 | } 378 | ast_config_destroy(cfg); 379 | 380 | return res; 381 | #else 382 | return 0; 383 | #endif 384 | } 385 | 386 | static int load_module(void) 387 | { 388 | if (parse_config(0)) { 389 | return AST_MODULE_LOAD_DECLINE; 390 | } 391 | 392 | opus_f.format = ast_format_slin48; 393 | if (ast_format_def_register(&opus_f)) { 394 | return AST_MODULE_LOAD_FAILURE; 395 | } 396 | 397 | return AST_MODULE_LOAD_SUCCESS; 398 | } 399 | 400 | static int reload_module(void) 401 | { 402 | if (parse_config(1)) { 403 | return AST_MODULE_LOAD_DECLINE; 404 | } 405 | 406 | return AST_MODULE_LOAD_SUCCESS; 407 | } 408 | 409 | static int unload_module(void) 410 | { 411 | return ast_format_def_unregister(opus_f.name); 412 | } 413 | 414 | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "OGG/Opus audio", 415 | .support_level = AST_MODULE_SUPPORT_CORE, 416 | .load = load_module, 417 | .reload = reload_module, 418 | .unload = unload_module, 419 | .load_pri = AST_MODPRI_APP_DEPEND 420 | ); 421 | -------------------------------------------------------------------------------- /formats/format_vp8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2014, Lorenzo Miniero 5 | * 6 | * Lorenzo Miniero 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | /*! \file 20 | * 21 | * \brief Save to raw, headerless VP8 data. 22 | * 23 | * \author Lorenzo Miniero 24 | * 25 | * \note Basically a "clone" of the H.264 passthrough format 26 | * 27 | * \arg File name extension: VP8 28 | * \ingroup formats 29 | * \arg See \ref AstVideo 30 | */ 31 | 32 | /*** MODULEINFO 33 | core 34 | ***/ 35 | 36 | #include "asterisk.h" 37 | 38 | #if defined(ASTERISK_REGISTER_FILE) 39 | ASTERISK_REGISTER_FILE() 40 | #elif defined(ASTERISK_FILE_VERSION) 41 | ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") 42 | #endif 43 | 44 | #include /* for htonl, htons, ntohl, ntohs */ 45 | #include /* for timeval = ast_filestream->ast_frame.delivery */ 46 | 47 | #include "asterisk/format_cache.h" /* for ast_format_vp8 */ 48 | #include "asterisk/frame.h" /* for ast_frame, AST_FRIENDLY_OFFSET */ 49 | #include "asterisk/logger.h" /* for ast_log, LOG_WARNING */ 50 | #include "asterisk/mod_format.h" /* for ast_filestream, ast_format_def */ 51 | #include "asterisk/module.h" 52 | 53 | /* VP8 passthrough */ 54 | #define FRAME_ENDED 0x8000 55 | 56 | #define BUF_SIZE 4096 57 | struct vp8_desc { 58 | unsigned int lastts; 59 | }; 60 | 61 | static int vp8_open(struct ast_filestream *s) 62 | { 63 | unsigned int ts; 64 | 65 | if (fread(&ts, 1, sizeof(ts), s->f) < sizeof(ts)) { 66 | ast_log(LOG_WARNING, "Empty file!\n"); 67 | return -1; 68 | } 69 | 70 | return 0; 71 | } 72 | 73 | static struct ast_frame *vp8_read(struct ast_filestream *s, int *whennext) 74 | { 75 | int res; 76 | int mark = 0; 77 | unsigned short len; 78 | unsigned int ts; 79 | struct vp8_desc *fs = (struct vp8_desc *) s->_private; 80 | 81 | /* Send a frame from the file to the appropriate channel */ 82 | if ((res = fread(&len, 1, sizeof(len), s->f)) < 1) { 83 | return NULL; 84 | } 85 | 86 | len = ntohs(len); 87 | mark = (len & FRAME_ENDED) ? 1 : 0; 88 | len &= 0x7fff; 89 | if (len > BUF_SIZE) { 90 | ast_log(LOG_WARNING, "Length %d is too long\n", len); 91 | len = BUF_SIZE; /* XXX truncate */ 92 | } 93 | s->fr.mallocd = 0; 94 | AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, len) 95 | if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) { 96 | if (res) { 97 | ast_log(LOG_WARNING, "Short read (%d of %d) (%s)!\n", res, len, strerror(errno)); 98 | } 99 | return NULL; 100 | } 101 | s->fr.samples = fs->lastts; 102 | s->fr.datalen = len; 103 | s->fr.subclass.frame_ending = mark; 104 | s->fr.delivery.tv_sec = 0; 105 | s->fr.delivery.tv_usec = 0; 106 | if ((res = fread(&ts, 1, sizeof(ts), s->f)) == sizeof(ts)) { 107 | fs->lastts = ntohl(ts); 108 | *whennext = fs->lastts * 4 / 45; 109 | } else { 110 | *whennext = 0; 111 | } 112 | return &s->fr; 113 | } 114 | 115 | static int vp8_write(struct ast_filestream *s, struct ast_frame *f) 116 | { 117 | int res; 118 | unsigned int ts; 119 | unsigned short len; 120 | int mark; 121 | 122 | if (f->frametype != AST_FRAME_VIDEO) { 123 | ast_log(LOG_WARNING, "Asked to write non-video frame!\n"); 124 | return -1; 125 | } 126 | 127 | mark = f->subclass.frame_ending ? FRAME_ENDED : 0; 128 | ts = htonl(f->samples); 129 | if ((res = fwrite(&ts, 1, sizeof(ts), s->f)) != sizeof(ts)) { 130 | ast_log(LOG_WARNING, "Bad write (%d/4): %s\n", res, strerror(errno)); 131 | return -1; 132 | } 133 | 134 | len = htons(f->datalen | mark); 135 | if ((res = fwrite(&len, 1, sizeof(len), s->f)) != sizeof(len)) { 136 | ast_log(LOG_WARNING, "Bad write (%d/2): %s\n", res, strerror(errno)); 137 | return -1; 138 | } 139 | 140 | if ((res = fwrite(f->data.ptr, 1, f->datalen, s->f)) != f->datalen) { 141 | ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); 142 | return -1; 143 | } 144 | 145 | return 0; 146 | } 147 | 148 | static int vp8_seek(struct ast_filestream *fs, off_t sample_offset, int whence) 149 | { 150 | /* No way Jose */ 151 | return -1; 152 | } 153 | 154 | static int vp8_trunc(struct ast_filestream *fs) 155 | { 156 | int fd; 157 | off_t cur; 158 | 159 | if ((fd = fileno(fs->f)) < 0) { 160 | ast_log(LOG_WARNING, "Unable to determine file descriptor for VP8 filestream %p: %s\n", fs, strerror(errno)); 161 | return -1; 162 | } 163 | 164 | if ((cur = ftello(fs->f)) < 0) { 165 | ast_log(LOG_WARNING, "Unable to determine current position in VP8 filestream %p: %s\n", fs, strerror(errno)); 166 | return -1; 167 | } 168 | 169 | /* Truncate file to current length */ 170 | return ftruncate(fd, cur); 171 | } 172 | 173 | static off_t vp8_tell(struct ast_filestream *fs) 174 | { 175 | off_t offset = ftell(fs->f); 176 | return offset; /* XXX totally bogus, needs fixing */ 177 | } 178 | 179 | static struct ast_format_def vp8_f = { 180 | .name = "VP8", 181 | .exts = "vp8", 182 | .open = vp8_open, 183 | .write = vp8_write, 184 | .seek = vp8_seek, 185 | .trunc = vp8_trunc, 186 | .tell = vp8_tell, 187 | .read = vp8_read, 188 | .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, 189 | .desc_size = sizeof(struct vp8_desc), 190 | }; 191 | 192 | static int load_module(void) 193 | { 194 | vp8_f.format = ast_format_vp8; 195 | if (ast_format_def_register(&vp8_f)) { 196 | return AST_MODULE_LOAD_FAILURE; 197 | } 198 | 199 | return AST_MODULE_LOAD_SUCCESS; 200 | } 201 | 202 | static int unload_module(void) 203 | { 204 | return ast_format_def_unregister(vp8_f.name); 205 | } 206 | 207 | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Raw VP8 data", 208 | .load = load_module, 209 | .unload = unload_module, 210 | .load_pri = AST_MODPRI_APP_DEPEND 211 | ); 212 | -------------------------------------------------------------------------------- /include/asterisk/opus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2013, Digium, Inc. 5 | * 6 | * Lorenzo Miniero 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | /*! 20 | * \file 21 | * \brief Codec opus externals and format attributes 22 | * 23 | * RFC - https://tools.ietf.org/rfc/rfc7587.txt 24 | */ 25 | #ifndef _AST_FORMAT_OPUS_H_ 26 | #define _AST_FORMAT_OPUS_H_ 27 | 28 | /*! \brief Maximum sampling rate an endpoint is capable of receiving */ 29 | #define CODEC_OPUS_ATTR_MAX_PLAYBACK_RATE "maxplaybackrate" 30 | /*! \brief An alias for maxplaybackrate (used in older versions) */ 31 | #define CODEC_OPUS_ATTR_MAX_CODED_AUDIO_BANDWIDTH "maxcodedaudiobandwidth" 32 | /*! \brief Maximum sampling rate an endpoint is capable of sending */ 33 | #define CODEC_OPUS_ATTR_SPROP_MAX_CAPTURE_RATE "sprop-maxcapturerate" 34 | /*! \brief Maximum duration of packet (in milliseconds) */ 35 | #define CODEC_OPUS_ATTR_MAX_PTIME "maxptime" 36 | /*! \brief Duration of packet (in milliseconds) */ 37 | #define CODEC_OPUS_ATTR_PTIME "ptime" 38 | /*! \brief Maximum average received bit rate (in bits per second) */ 39 | #define CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE "maxaveragebitrate" 40 | /*! \brief Decode stereo (1) vs mono (0) */ 41 | #define CODEC_OPUS_ATTR_STEREO "stereo" 42 | /*! \brief Likeliness of sender producing stereo (1) vs mono (0) */ 43 | #define CODEC_OPUS_ATTR_SPROP_STEREO "sprop-stereo" 44 | /*! \brief Decoder prefers a constant (1) vs variable (0) bitrate */ 45 | #define CODEC_OPUS_ATTR_CBR "cbr" 46 | /*! \brief Use forward error correction (1) or not (0) */ 47 | #define CODEC_OPUS_ATTR_FEC "useinbandfec" 48 | /*! \brief Use discontinuous transmission (1) or not (0) */ 49 | #define CODEC_OPUS_ATTR_DTX "usedtx" 50 | /*! \brief Custom data object */ 51 | #define CODEC_OPUS_ATTR_DATA "data" 52 | 53 | /*! \brief Default attribute values */ 54 | #define CODEC_OPUS_DEFAULT_SAMPLE_RATE 48000 55 | #define CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE 48000 56 | #define CODEC_OPUS_DEFAULT_MAX_PTIME 120 57 | #define CODEC_OPUS_DEFAULT_PTIME 20 58 | #define CODEC_OPUS_DEFAULT_BITRATE 510000 59 | #define CODEC_OPUS_DEFAULT_CBR 0 60 | #define CODEC_OPUS_DEFAULT_FEC 1 61 | #define CODEC_OPUS_DEFAULT_DTX 0 62 | #define CODEC_OPUS_DEFAULT_STEREO 0 63 | 64 | #endif /* _AST_FORMAT_OPUS_H */ 65 | -------------------------------------------------------------------------------- /res/res_format_attr_opus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2013, Digium, Inc. 5 | * 6 | * Lorenzo Miniero 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | /*! 20 | * \file 21 | * \brief Opus format attribute interface 22 | * 23 | * \author Lorenzo Miniero 24 | */ 25 | 26 | /*** MODULEINFO 27 | core 28 | ***/ 29 | 30 | #include "asterisk.h" 31 | 32 | #if defined(ASTERISK_REGISTER_FILE) 33 | ASTERISK_REGISTER_FILE() 34 | #elif defined(ASTERISK_FILE_VERSION) 35 | ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") 36 | #endif 37 | 38 | #include "asterisk/module.h" 39 | #include "asterisk/format.h" 40 | #include "asterisk/logger.h" /* for ast_log, LOG_WARNING */ 41 | #include "asterisk/opus.h" /* for CODEC_OPUS_DEFAULT_* */ 42 | #include "asterisk/strings.h" /* for ast_str_append */ 43 | #include "asterisk/utils.h" /* for MIN, ast_malloc, ast_free */ 44 | 45 | /*! 46 | * \brief Opus attribute structure. 47 | * 48 | * \note http://tools.ietf.org/html/rfc7587#section-6 49 | */ 50 | struct opus_attr { 51 | unsigned int maxbitrate; 52 | unsigned int maxplayrate; 53 | unsigned int unused; /* was minptime, kept for binary compatibility */ 54 | unsigned int stereo; 55 | unsigned int cbr; 56 | unsigned int fec; 57 | unsigned int dtx; 58 | unsigned int spropmaxcapturerate; 59 | unsigned int spropstereo; 60 | }; 61 | 62 | static struct opus_attr default_opus_attr = { 63 | .maxplayrate = CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE, 64 | .spropmaxcapturerate = CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE, 65 | .maxbitrate = CODEC_OPUS_DEFAULT_BITRATE, 66 | .stereo = CODEC_OPUS_DEFAULT_STEREO, 67 | .spropstereo = CODEC_OPUS_DEFAULT_STEREO, 68 | .cbr = CODEC_OPUS_DEFAULT_CBR, 69 | .fec = CODEC_OPUS_DEFAULT_FEC, 70 | .dtx = CODEC_OPUS_DEFAULT_DTX, 71 | }; 72 | 73 | static void opus_destroy(struct ast_format *format) 74 | { 75 | struct opus_attr *attr = ast_format_get_attribute_data(format); 76 | 77 | ast_free(attr); 78 | } 79 | 80 | static int opus_clone(const struct ast_format *src, struct ast_format *dst) 81 | { 82 | struct opus_attr *original = ast_format_get_attribute_data(src); 83 | struct opus_attr *attr = ast_malloc(sizeof(*attr)); 84 | 85 | if (!attr) { 86 | return -1; 87 | } 88 | 89 | if (original) { 90 | *attr = *original; 91 | } else { 92 | *attr = default_opus_attr; 93 | } 94 | 95 | ast_format_set_attribute_data(dst, attr); 96 | 97 | return 0; 98 | } 99 | 100 | static struct ast_format *opus_parse_sdp_fmtp(const struct ast_format *format, const char *attributes) 101 | { 102 | struct ast_format *cloned; 103 | struct opus_attr *attr; 104 | const char *kvp; 105 | unsigned int val; 106 | 107 | cloned = ast_format_clone(format); 108 | if (!cloned) { 109 | return NULL; 110 | } 111 | attr = ast_format_get_attribute_data(cloned); 112 | 113 | if ((kvp = strstr(attributes, "maxplaybackrate")) && sscanf(kvp, "maxplaybackrate=%30u", &val) == 1) { 114 | attr->maxplayrate = val; 115 | } else { 116 | attr->maxplayrate = 48000; 117 | } 118 | 119 | if ((kvp = strstr(attributes, "sprop-maxcapturerate")) && sscanf(kvp, "sprop-maxcapturerate=%30u", &val) == 1) { 120 | attr->spropmaxcapturerate = val; 121 | } else { 122 | attr->spropmaxcapturerate = 48000; 123 | } 124 | 125 | if ((kvp = strstr(attributes, "maxaveragebitrate")) && sscanf(kvp, "maxaveragebitrate=%30u", &val) == 1) { 126 | attr->maxbitrate = val; 127 | } else { 128 | attr->maxbitrate = 510000; 129 | } 130 | 131 | if (!strncmp(attributes, "stereo=1", 8)) { 132 | attr->stereo = 1; 133 | } else if (strstr(attributes, " stereo=1")) { 134 | attr->stereo = 1; 135 | } else if (strstr(attributes, ";stereo=1")) { 136 | attr->stereo = 1; 137 | } else { 138 | attr->stereo = 0; 139 | } 140 | 141 | if (strstr(attributes, "sprop-stereo=1")) { 142 | attr->spropstereo = 1; 143 | } else { 144 | attr->spropstereo = 0; 145 | } 146 | 147 | if (strstr(attributes, "cbr=1")) { 148 | attr->cbr = 1; 149 | } else { 150 | attr->cbr = 0; 151 | } 152 | 153 | if (strstr(attributes, "useinbandfec=1")) { 154 | attr->fec = 1; 155 | } else { 156 | attr->fec = 0; 157 | } 158 | 159 | if (strstr(attributes, "usedtx=1")) { 160 | attr->dtx = 1; 161 | } else { 162 | attr->dtx = 0; 163 | } 164 | 165 | return cloned; 166 | } 167 | 168 | static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str) 169 | { 170 | struct opus_attr *attr = ast_format_get_attribute_data(format); 171 | int added = 0; 172 | 173 | if (!attr) { 174 | /* 175 | * (Only) cached formats do not have attribute data assigned because 176 | * they were created before this attribute module was registered. 177 | * Therefore, we assume the default attribute values here. 178 | */ 179 | attr = &default_opus_attr; 180 | } 181 | 182 | if (48000 != attr->maxplayrate) { 183 | if (added) { 184 | ast_str_append(str, 0, ";"); 185 | } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { 186 | added = 1; 187 | } 188 | ast_str_append(str, 0, "maxplaybackrate=%u", attr->maxplayrate); 189 | } 190 | 191 | if (48000 != attr->spropmaxcapturerate) { 192 | if (added) { 193 | ast_str_append(str, 0, ";"); 194 | } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { 195 | added = 1; 196 | } 197 | ast_str_append(str, 0, "sprop-maxcapturerate=%u", attr->spropmaxcapturerate); 198 | } 199 | 200 | if (510000 != attr->maxbitrate) { 201 | if (added) { 202 | ast_str_append(str, 0, ";"); 203 | } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { 204 | added = 1; 205 | } 206 | ast_str_append(str, 0, "maxaveragebitrate=%u", attr->maxbitrate); 207 | } 208 | 209 | if (0 != attr->stereo) { 210 | if (added) { 211 | ast_str_append(str, 0, ";"); 212 | } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { 213 | added = 1; 214 | } 215 | ast_str_append(str, 0, "stereo=%u", attr->stereo); 216 | } 217 | 218 | if (0 != attr->spropstereo) { 219 | if (added) { 220 | ast_str_append(str, 0, ";"); 221 | } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { 222 | added = 1; 223 | } 224 | ast_str_append(str, 0, "sprop-stereo=%u", attr->spropstereo); 225 | } 226 | 227 | if (0 != attr->cbr) { 228 | if (added) { 229 | ast_str_append(str, 0, ";"); 230 | } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { 231 | added = 1; 232 | } 233 | ast_str_append(str, 0, "cbr=%u", attr->cbr); 234 | } 235 | 236 | if (0 != attr->fec) { 237 | if (added) { 238 | ast_str_append(str, 0, ";"); 239 | } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { 240 | added = 1; 241 | } 242 | ast_str_append(str, 0, "useinbandfec=%u", attr->fec); 243 | } 244 | 245 | if (0 != attr->dtx) { 246 | if (added) { 247 | ast_str_append(str, 0, ";"); 248 | } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { 249 | added = 1; 250 | } 251 | ast_str_append(str, 0, "usedtx=%u", attr->dtx); 252 | } 253 | 254 | if (added) { 255 | ast_str_append(str, 0, "\r\n"); 256 | } 257 | } 258 | 259 | static struct ast_format *opus_getjoint(const struct ast_format *format1, const struct ast_format *format2) 260 | { 261 | struct opus_attr *attr1 = ast_format_get_attribute_data(format1); 262 | struct opus_attr *attr2 = ast_format_get_attribute_data(format2); 263 | struct ast_format *jointformat; 264 | struct opus_attr *attr_res; 265 | 266 | if (!attr1) { 267 | attr1 = &default_opus_attr; 268 | } 269 | 270 | if (!attr2) { 271 | attr2 = &default_opus_attr; 272 | } 273 | 274 | jointformat = ast_format_clone(format1); 275 | if (!jointformat) { 276 | return NULL; 277 | } 278 | attr_res = ast_format_get_attribute_data(jointformat); 279 | 280 | attr_res->dtx = attr1->dtx || attr2->dtx ? 1 : 0; 281 | 282 | /* Only do FEC if both sides want it. If a peer specifically requests not 283 | * to receive with FEC, it may be a waste of bandwidth. */ 284 | attr_res->fec = attr1->fec && attr2->fec ? 1 : 0; 285 | 286 | attr_res->cbr = attr1->cbr || attr2->cbr ? 1 : 0; 287 | attr_res->spropstereo = attr1->spropstereo || attr2->spropstereo ? 1 : 0; 288 | 289 | /* Only do stereo if both sides want it. If a peer specifically requests not 290 | * to receive stereo signals, it may be a waste of bandwidth. */ 291 | attr_res->stereo = attr1->stereo && attr2->stereo ? 1 : 0; 292 | 293 | attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate); 294 | attr_res->spropmaxcapturerate = MIN(attr1->spropmaxcapturerate, attr2->spropmaxcapturerate); 295 | attr_res->maxplayrate = MIN(attr1->maxplayrate, attr2->maxplayrate); 296 | 297 | return jointformat; 298 | } 299 | 300 | static struct ast_format *opus_set(const struct ast_format *format, const char *name, const char *value) 301 | { 302 | struct ast_format *cloned; 303 | struct opus_attr *attr; 304 | unsigned int val; 305 | 306 | if (sscanf(value, "%30u", &val) != 1) { 307 | ast_log(LOG_WARNING, "Unknown value '%s' for attribute type '%s'\n", 308 | value, name); 309 | return NULL; 310 | } 311 | 312 | cloned = ast_format_clone(format); 313 | if (!cloned) { 314 | return NULL; 315 | } 316 | attr = ast_format_get_attribute_data(cloned); 317 | 318 | if (!strcasecmp(name, "max_bitrate")) { 319 | attr->maxbitrate = val; 320 | } else if (!strcasecmp(name, "max_playrate")) { 321 | attr->maxplayrate = val; 322 | } else if (!strcasecmp(name, "minptime")) { 323 | attr->unused = val; 324 | } else if (!strcasecmp(name, "stereo")) { 325 | attr->stereo = val; 326 | } else if (!strcasecmp(name, "cbr")) { 327 | attr->cbr = val; 328 | } else if (!strcasecmp(name, "fec")) { 329 | attr->fec = val; 330 | } else if (!strcasecmp(name, "dtx")) { 331 | attr->dtx = val; 332 | } else if (!strcasecmp(name, "sprop_capture_rate")) { 333 | attr->spropmaxcapturerate = val; 334 | } else if (!strcasecmp(name, "sprop_stereo")) { 335 | attr->spropstereo = val; 336 | } else { 337 | ast_log(LOG_WARNING, "unknown attribute type %s\n", name); 338 | } 339 | 340 | return cloned; 341 | } 342 | 343 | static struct ast_format_interface opus_interface = { 344 | .format_destroy = opus_destroy, 345 | .format_clone = opus_clone, 346 | .format_get_joint = opus_getjoint, 347 | .format_attribute_set = opus_set, 348 | .format_parse_sdp_fmtp = opus_parse_sdp_fmtp, 349 | .format_generate_sdp_fmtp = opus_generate_sdp_fmtp, 350 | }; 351 | 352 | static int load_module(void) 353 | { 354 | if (ast_format_interface_register("opus", &opus_interface)) { 355 | return AST_MODULE_LOAD_DECLINE; 356 | } 357 | 358 | return AST_MODULE_LOAD_SUCCESS; 359 | } 360 | 361 | static int unload_module(void) 362 | { 363 | return 0; 364 | } 365 | 366 | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Opus Format Attribute Module", 367 | .support_level = AST_MODULE_SUPPORT_CORE, 368 | .load = load_module, 369 | .unload = unload_module, 370 | .load_pri = AST_MODPRI_CHANNEL_DEPEND, 371 | ); 372 | --------------------------------------------------------------------------------